C陷阱与缺陷代码分析之第2章语法陷阱
作者:刘昊昱
博客:http://blog.csdn.net/liuhaoyutz
陷阱1 理解函数声明
作者提出一个问题:有一个首地址为0的函数,该函数返回值类型为void,没有参数。怎样用C语言的语句调用这个函数?
答案是(*(void (*)())0)();
要理解这个调用形式,要清楚如下两个问题:
一是函数指针。
假设fp是一个函数指针,则调用fp所指向的函数的方法是
(*fp)();
因为fp是一个函数指针,所以*fp是该指针所指向的函数,所以(*fp)()就是调用该函数的方式。ANSI C允许将(*fp)()简写为fp(),fp()也是我们比较常见的形式,但是一定要知道这种写法是一种简写形式。例如prinf()函数,printf就是函数指针,它的完整形式是(*printf)()。为了说明这个问题,我们来看一个测试程序page17.c,代码如下:
1#include <stdio.h>
2
3int main()
4{
5 printf("test1\n");
6 (*printf)("test2\n");
7
8 return 0;
9}
编译运行结果如下:
二是强制类型转换符的声明方式。
某类型的强制类型转换符,只需要把该类型变量声明中的变量名和声明末尾的分号去掉,再将剩余的部分用一个括号封装起来即可。例如,声明一个int型指针变量的方式是
int *p;
按照上面的原则,int型指针强制类型转换符就是把变量名p和末尾的分号去掉,再把剩余的部分用一个括号封装起来,即(int *)。
同理,声明一个返回值为void,没有参数的函数指针变量f的方式是:
void (*f)();
按照上面的原则,返回值为void,没有参数的函数指针类型强制转换符就是把变量名f和最后的分号去掉,再把剩余的部分用一个括号封装起来,即(void (*)())。
有了上面的预备知识,我们可以来看作者提出的问题了。首地址为0的函数,也就是函数指针的值为0,函数返回值类型为void,没有参数。所以我们把0强制转换为(void (*)())类型就是该函数的函数指针,有了函数指针,要调用该函数,则是(*(void (*)())0)();
如果使用typedef能够使表述更加清晰:
typedef void (*funcptr)();
(*(funcptr)0)();
作者举的第二个例子是signal函数,其函数声明如下:
void (*signal(int, void(*)(int)))(int);
怎样来理解这个函数声明呢?
signal函数有两个参数,第一个参数是一个整数,代表需要“被捕获”的特定信号。第二个参数是一个函数指针,它是信号处理函数指针,它的返回值类型为void,该信号处理函数同样有一个int型参数代表要处理的信号。
让我们从信号处理函数开始,信号处理函数的函数指针声明如下:
void (*sfp)(int);
信号处理函数指针类型可以通过把指针变量名sfp和最后的分号去掉得到,即:
void (*)(int)
signal函数的返回值是原来的信号处理函数指针,即singnal函数的返回值类型是void(*)(int)。
综上所述可知,signal函数的声明形式应该是:
void (*signal(int, void(*)(int)))(int);
同样地,使用typedef可以简化signal函数的声明:
typedef void (*HANDLER)(int);
HANDLER signal (int, HANDLER);
陷阱二运算符的优先级
记住C语言运算符的优先级是非常有益的,但是,C语言运算符优先级多达15个,记住它们并不是一件容易的事。完整的C语言运算符优先级如下表所示:
|
优先级 |
运算符 |
名称或含义 |
使用形式 |
结合方向 |
说明 |
|
[] |
数组下标 |
数组名[常量表达式] |
左到右 |
||
|
() |
圆括号 |
(表达式)/函数名(形参表) |
|||
|
. |
成员选择(对象) |
对象.成员名 |
|||
|
-> |
成员选择(指针) |
对象指针->成员名 |
|||
|
- |
负号运算符 |
-表达式 |
右到左 |
单目运算符 |
|
|
(类型) |
强制类型转换 |
(数据类型)表达式 |
|||
|
++ |
自增运算符 |
++变量名/变量名++ |
单目运算符 |
||
|
-- |
自减运算符 |
--变量名/变量名-- |
单目运算符 |
||
|
* |
取值运算符 |
*指针变量 |
单目运算符 |
||
|
& |
取地址运算符 |
&变量名 |
单目运算符 |
||
|
! |
逻辑非运算符 |
!表达式 |
单目运算符 |
||
|
~ |
按位取反运算符 |
~表达式 |
单目运算符 |
||
|
sizeof |
长度运算符 |
sizeof(表达式) |
|||
|
/ |
除 |
表达式/表达式 |
左到右 |
双目运算符 |
|
|
* |
乘 |
表达式*表达式 |
双目运算符 |
||
|
% |
余数(取模) |
整型表达式/整型表达式 |
双目运算符 |
||
|
+ |
加 |
表达式+表达式 |
左到右 |
双目运算符 |
|
|
- |
减 |
表达式-表达式 |
双目运算符 |
||
|
<< |
左移 |
变量<<表达式 |
左到右 |
双目运算符 |
|
|
>> |
右移 |
变量>>表达式 |
双目运算符 |
||
|
> |
大于 |
表达式>表达式 |
左到右 |
双目运算符 |
|
|
>= |
大于等于 |
表达式>=表达式 |
双目运算符 |
||
|
< |
小于 |
表达式<表达式 |
双目运算符 |
||
|
<= |
小于等于 |
表达式<=表达式 |
双目运算符 |
||
|
== |
等于 |
表达式==表达式 |
左到右 |
双目运算符 |
|
|
!= |
不等于 |
表达式!= 表达式 |
双目运算符 |
||
|
& |
按位与 |
表达式&表达式 |
左到右 |
双目运算符 |
|
|
^ |
按位异或 |
表达式^表达式 |
左到右 |
双目运算符 |
|
|
| |
按位或 |
表达式|表达式 |
左到右 |
双目运算符 |
|
|
&& |
逻辑与 |
表达式&&表达式 |
左到右 |
双目运算符 |
|
|
|| |
逻辑或 |
表达式||表达式 |
左到右 |
双目运算符 |
|
|
?: |
条件运算符 |
表达式1? 表达式2: 表达式 |
右到左 |
三目运算符 |
|
|
= |
赋值运算符 |
变量=表达式 |
右到左 |
||
|
/= |
除后赋值 |
变量/=表达式 |
|||
|
*= |
乘后赋值 |
变量*=表达式 |
|||
|
%= |
取模后赋值 |
变量%=表达式 |
|||
|
+= |
加后赋值 |
变量+=表达式 |
|||
|
-= |
减后赋值 |
变量-=表达式 |
|||
|
<<= |
左移后赋值 |
变量<<=表达式 |
|||
|
>>= |
右移后赋值 |
变量>>=表达式 |
|||
|
&= |
按位与后赋值 |
变量&=表达式 |
|||
|
^= |
按位异或后赋值 |
变量^=表达式 |
|||
|
|= |
按位或后赋值 |
变量|=表达式 |
|||
|
, |
逗号运算符 |
表达式,表达式,… |
左到右 |
从左向右顺序运算 |
如果把这些运算符恰当分组,并且理解了各组运算符之间的相对优先级,那么这张表其实不难记住。
第一:
优先级最高者其实并不是真正意义上的运算符,包括:数组下标、函数调用操作符,结构成员选择符。它们的优先级是1级,都是自左向右结合,因此a.b.c的含义是(a.b).c,而不是a.(b.c)。
第二:
单目运算符的优先级仅次于前述运算符,它们的优先级是2级。在所有真正意义上的运算符中,它们的优先级最高。单目运算符是从右向左结合的,因此*p++会被编译器解释成*(p++),即取指针p所指向的对象,然后将p递增1,而不是(*p)++,即取指针p所指向的对象,然后将该对象的值加1。
第三:
优先级比单目运算符低的,接下来就是双目运算符。
在双目运算符中,算术运算符优先级最高(乘、除、取余为3级,加、减为4级),
移位运算符次之(左移>>、右移<<,为5级),
关系运算符再次之(如>、<、<=等等,为6级,==和!=,为7级),
接着是逻辑运算符(如按位与&、按位或|、逻辑与&&、逻辑或||,等等),
接下来是条件运算符(?:其实这是一个三目运算符),
赋值运算符(=,/=,*=等等),
优先级最低的是逗号运算符(,)。
我们需要记住的最重要的一点是:
算术运算符(加减乘除)>
移位运算符(左移>>、右移<<) >
关系运算符(大于>、小于<、等于==,等等) >
逻辑运算符(按位与&、按位或|、逻辑与&&、逻辑或||,等等)
C陷阱与缺陷代码分析之第2章语法陷阱的更多相关文章
- C陷阱与缺陷代码分析之第1章词法陷阱
作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 编译器中负责将程序分解为一个一个符号的部分,称为“词法分析器”.下面看一个例子: if(x > big) bi ...
- [C陷阱和缺陷] 第2章 语法“陷阱”
第2章 语法陷阱 2.1 理解函数声明 当计算机启动时,硬件将调用首地址为0位置的子例程,为了模拟开机时的情形,必须设计出一个C语言,以显示调用该子例程,经过一段时间的思考,得出语句如下: ( * ...
- c缺陷与陷阱笔记-第二章 语法陷阱
1.函数的调用和番薯返回值是函数指针的声明 定义一个函数指针,例如 int (*fp)(float),这个函数的返回值是Int,参数是1个float类型,调用这个函数的方法是 (*fp)(),还有f ...
- C语言学习书籍推荐《C陷阱与缺陷》下载
下载地址:点我 凯尼格 (作者), 高巍 (译者) <C和C++经典著作:C陷阱与缺陷>适合有一定经验的C程序员阅读学习,即便你是C编程高手,<C和C++经典著作:C陷阱与缺陷> ...
- 阅读《C陷阱与缺陷》的知识增量
版权声明:本文为Focustc原创文章.转载请注明作者及出处. https://blog.csdn.net/caozhankui/article/details/35925939 看完<C陷阱与 ...
- C陷阱与缺陷(二)
第二章 语法陷阱 2.1 理解函数声明 (*(void(*)())0)();任何C变量的声明都由两部分组成:类型以及一组类似表达式的声明符.一旦我们知道了如何声明一个给定类型的变量,那么该类型的类型转 ...
- 我的《C陷阱与缺陷》读书笔记
第一章 词法“陷阱” 1. =不同于== if(x = y) break; 实际上是将y赋给x,再检查x是否为0. 如果真的是这样预期,那么应该改为: if((x = y) != 0) break; ...
- 《C陷阱与缺陷》读书笔记
1. 词法“陷阱” = 不同于 == , 可以通过if( 1 == a )来避免 & | 不同于 && || 词法分析中的“贪心法” 编译器将程序分解成符号的方法是,从左到右一 ...
- 常用 Java 静态代码分析工具的分析与比较
常用 Java 静态代码分析工具的分析与比较 简介: 本文首先介绍了静态代码分析的基 本概念及主要技术,随后分别介绍了现有 4 种主流 Java 静态代码分析工具 (Checkstyle,FindBu ...
随机推荐
- 在VS2010上使用C#调用非托管C++生成的DLL文件
背景 在项目过程中,有时候你需要调用非C#编写的DLL文件,尤其在使用一些第三方通讯组件的时候,通过C#来开发应用软件时,就需要利用DllImport特性进行方法调用.本篇文章将引导你快速理解这个调用 ...
- foreach学习笔记
对集合进行遍历 只能获取集合元素,但是不能对集合进行操作. 迭代器除了遍历,还可以进行remove的动作. 如果是用ListIterator,还可以在遍历过程中进行增删改查的动作. for(Strin ...
- Qt Creator插件工作流程代码走读
Qt Creator有个很风骚的插件管理器PluginManager,还有个很骚包的插件说明PluginSpec.基本上,所有的Qt程序的入口都是传统的C程序一样,代码流程从main()函数开始. ...
- 新发现的mbstowcs, mbstowcs_s函数,转换多字节到宽字符
http://en.cppreference.com/w/c/string/multibyte/mbstowcs https://msdn.microsoft.com/fr-fr/library/ey ...
- Spring Cache抽象详解
缓存简介 缓存,我的理解是:让数据更接近于使用者:工作机制是:先从缓存中读取数据,如果没有再从慢速设备上读取实际数据(数据也会存入缓存):缓存什么:那些经常读取且不经常修改的数据/那些昂贵(CPU/I ...
- use utf8
[root@wx03 0724]# cat a2.pl use Encode; my $a=<STDIN>; my $b=encode_utf8('微信'); print "\$ ...
- [每日一题] 11gOCP 1z0-052 :2013-09-14 repeated parsing activity.................................A70
转载请注明出处:http://blog.csdn.net/guoyjoe/article/details/11699299 正确答案:D SQL语句的执行过程: 1.客户端输入sql语句update ...
- Robot Framework学习路线
0. 官方网站 http://robotframework.org/ 所有资料都来自这里,从这里找到必要的链接,从而深入其中的细节. 1. Quick Start Guide https://co ...
- Ultra-QuickSort(归并排序+离散化树状数组)
Ultra-QuickSort Time Limit: 7000MS Memory Limit: 65536K Total Submissions: 50517 Accepted: 18534 ...
- android 仿小米icon处理,加阴影和边框
本人自己在做一个launcher,所以须要处理icon,加阴影和边框等.这仅仅是一种处理方法,其它的处理方法类似. 源码: https://github.com/com314159/LauncherI ...