先来一个热身:
#define ABC(x) x*x
#include"stdio.h"
main()
{int a=3,b;
b=ABC(a+1);
printf("b=%d", b); }
输出是7,原因是宏只是简单替换,不包括括号
一、词法陷阱
1. c编译器是从左到右读入字符的(大嘴法)
2. int a = 0111;//代表8进制数,十进制是73
3. int i = 'ab';//代表将字符a和b的ascll码赋值给i(i通常有多个字节,而字符型1个字节)
4. %d整型输出,%ld长整型输出,
%o以八进制数形式输出整数,
%x以十六进制数形式输出整数,
%u以十进制数输出unsigned型数据(无符号数)。
%c用来输出一个字符,
%s用来输出一个字符串,
%f用来输出实数,以小数形式输出,
%e以指数形式输出实数,
%g根据大小自动选f格式或e格式,且不输出无意义的零。
5. 对于 /*/*/0*/**/1,
支持注释嵌套的编译器(注释里可以有注释)解释为1
不支持注释嵌套的编译器(/*后面都是注释直到*/)解释为0*1
c的编译器一般不支持嵌套注释,原因也许是这样更复杂(既然是注释,注释内的所有文本都被视为注释而被编译器无视之,凭什么注释内的/*,也就是注释开始符号要特殊对待呢?)
二、 语法陷阱
1. int *g(),(*h)();//g是一个返回值类型为指向整形的指针的函数,h是一个函数指针,其指向的函数的返回值是int型
2. ANSI C标准允许在用函数指针fp调用函数时,把(*fp)()简写为fp()
3. void (*fp)();//定义一个函数指针,其指向一个返回值为void的函数
(void (*)() )0将常数0转型为“指向返回值为void的函数的指针”
(* (void (*)() )0)();//调用一个首地址为0的函数,0被转换成了“指向返回值为void的函数的指针”,并以(*fp)()的方式调用
4. 逻辑运算符包括与、或、非,赋值运算符优先级仅比“,”要高,且是从右到左
5. 从右到左的还有有高优先级的!、~、++、--、-、(type)*、&、sizeof以及赋值运算符=
6. *buf++ = c;//先++后*
7. 逗号运算符是逗号分开的表达式的值分别结算,但整个表达式的值是最后一个表达式的值。
8. switch语句中记得break,也有故意不break的,例如:
case 减:
num= -num;
case 加
...
9. f();//调用函数
f;//计算函数f的地址,但不调用
三、 语义陷阱
1. a[i]==*(a+i)==*(i+a)==i[a]//(相当神奇)
i[4][7]==*(i[4]+7)==*(*(i+4)+7)
2. int (*a)[10];//定义一个指向数组的指针,该数组拥有10个int型元素
3. 为字符串malloc内存时,要比原句子多申请一个字节(因为字符串以'\0'结尾)
4. 尽量用for(i = 0; i < 10; i++)而不用小于等于9。原因是10-0=10相当于元素个数;元素个数为0时就是“i<0”
5. y[i++] = x[i]的行为不确定,根据编译器而异
四、 连接
1. 最好对函数进行声明并在声明时明确参数和返回值的类型,否则会默认判断为int或者double
五、 预处理器
1. 宏是直接替代,必须加各种括号来预防优先级问题,如#define abs() ((x>=0)?(x):-(x))
2. 对于#define abs(x) x>0?x:-x,有abs(a)+1展开后为a>0?a:-a+1
3. 宏定义可以以“\”作为换行,例如:
#define MAX(a,b) \
((a) <(b) ? (b) : (a))
char buf[128];
strcpy(buf, "1234567890ab "
"cdefg ");
等同于:
char buf[128];
strcpy(buf, "1234567890abcdefg");
4. 对于#define T1 int *,T1 a,b相当于 T1 * a,b;