1、
while ( c= ' ' || c== '\t' || c== '\n')
c=getc(f);
程序员有时候会将=写成==,因为赋值运算符=的优先级要低于逻辑运算符||,因此实际上上述表达式是把 **’ ’ || c== ‘\t’ || c== ‘\n’**的值赋值给c,因为’ '不等于0(ASCII码值为32),所以无论变量c为何值,上述表达式的结果都是1,因此循环将一直下去知道文件结束。
2、
if((filedesc == open (argv[i], 0))<0)
error();
这类错误是把赋值运算符误写成比较运算符的类型,open函数若执行成功,则返回0或正数,若open函数执行失败,将返回-1。因为比较运算符==的结果只能是0或1,所以函数error()将没有机会被调用。若代码执行,似乎一切正常,除了变量filedsec的值不再是open函数的返回值,在某些编译器在遇到这种情况下,会警告与0比较无效。
3、整型常量
如果一个整型常量的第一个数字是0,那么该常量将被视为八进制数。因此10与010的含义截然不同。
有时候在上下文中为了格式对齐的需要,可能无意中将十进制数写成了八进制,例如:
struct
{
int part_number;
char *description;
}parttab[]=
{
046,"left-handed widget",
047,"right-handed widget",
125,"frammis"
};
4、理解函数声明
(*(void(*)())0)();
如上所示的语句,一般很难看懂,可以利用下面方法来解析:构造这类表达式其实只有一条简单的规则:按照使用的方式来声明。
任何C语言变量的声明都是由两部分组成:类型以及一组类似表达式的声明符。声明符从表面上看与表达式有些类似,对它求值应该返回一个声明中给定类型的结果。简单的声明符为单个变量:
float f,g;
因为声明符与表达式相似,可以在声明符中任意使用括号:
float ((f));
同样的逻辑也适用于函数和指针类型的声明:
float ff();
这个声明的含义,表达式ff()求值的结果是一个浮点数,ff是一个返回值为浮点类型的函数。
float *pf;
类似的,这个声明的含义表示*pf是一个浮点数,也就是说,pf是一个指向浮点数的指针。
以上这些形式在声明中可以组合起来。
float *g(),(*h)();
上式表明*g()与(*h)()是浮点表达式。*g()中g是一个,该函数的返回值类型为指向浮点数的指针。(*h)()中h是一个函数指针,h所指向的函数的返回值为浮点类型。
在了解如何声明一个给定类型的变量,那么该类型的类型转换符就很容易得到:只需要把声明中的变量名和声明末尾的分好去掉,再将剩余的部分用一个括号整个“封装”起来即可:例如
float (*h)();
表示h是一个指针函数,h是一个指向返回值为float类型的函数的指针,h所指向的函数的返回值是float类型。
(float (*)());
表示一个“指向返回值为浮点类型的函数的指针”的类型转换符。
现在,开始利用两步分析 (*(void(*)())0)();
step1:假定变量fp是一个函数指针,则调用fp所指向的函数方法如下:
(*fp)();
因为fp是一个函数指针,故*fp就是该指针所指向的函数,所以(*fp)()就是调用该函数的方式。ANSIC标准允许程序员将上式简写为fp(),但是要记住这种写法只是一个简写形式。
step2:找到一个恰当的表达式来替换fp,最简单的转换如下:
(*0)();
但是,上式并不能生效,因为运算符*必须要一个指针来做操作数,这个指针应该是一个函数指针,这样经运算符作用后的结果才能够作为函数被调用。因此,必须对上式中的0作类型转换,转换后的类型可以大致描述为:指向返回值为void类型的函数的指针。
若fp是指向返回值为void类型的函数的指针,那么(*fp)()的值为void,fp的声明如下:
void (*fp)();
因此,可以用下式来完成调用存储位置为0的子例程:
void (*fp)();
(*fp)();
下面对一个常数进行类型转换,将其转换为该变量的类型:只需要在变量声明中将变量名去掉即可。因此,将常数0转换为“指向返回值为void的函数的指针”类型:
(void (*)())0;
因此,可以用(void (*)())0;
来替换fp:
(*(void (*)())0)();
注:利用typedef能够使表示更加清晰:
typedef void (*funcptr)();
(*(funcptr)0)();
5、运算符优先级
while (c=getc(in) != EOF)
putc(c,out);
因为关系运算符的优先级要高于赋值运算符,所以上式c的实际值是函数getc(in)的返回值与EOF比较的结果。因此,最后得到的文件中只是包括了一组二进制值为1的字节流。
正确写法:
while ((c=getc(in)) != EOF)
putc(c,out);
6、函数调用
如果函数f是一个函数:
f();
是一个函数调用语句;
f;
是一个什么都不做的语句,更准确的说,这个语句是计算f的地址,却并没有调用f函数。
7、指针与数组
c语言中的数组注意的地方有一下两点:
(1)c语言中数组只有一维数组,而且数组大小必须在编译期间就作为一个常数确定下来。然而数组中的元素可以是任何类型的对象,当然可以是另外一个数组。这样就可以很容的形成一个多维数组。
(2)对于一个数组,只有两个操作:确定该数组的大小,以及获得指向该数组下标为0的元素指针。其他有关数组的操作,即使看上去是以数组下标进行运算的,实际上都是通过指针进行的。
int array[12][31];
这个语句声明了array是一个数组,该数组拥有12个数组类型的元素,每个元素是一个拥有31个整型元素的数组。
sizeof(array)=31*12=372;
如果array不是用于sizeof的操作数,而是用于其他场合。那么array总是被转换成一个指向array数组的起始元素的指针。
array[4]表示一个有着31个整形元素的数组。
sizeof(array[4])=31*sizeof(int)=31*4=124;
指针p指向了数组array[4]中下标为0的元素:
int *p;
p=array[4];
整型i得到数组array某个位置的值:
int i;
i=array[4][7];
i=*(array[4]+7);
i=*(*(array+4)+7);
下式是错误的:因为array是一个二维数组,即数组的数组,在此处array会将其转换成一个指向数组的指针;而p是一个指向整型变量的指针,将一种类型的指针赋值到另一种类型的指针是非法的。
p=array;
若要声明一种指向数组的指针的方法,如下:
int (*ap)[31];
上述语句声明了*ap是一个拥有31个整型元素的数组,ap就是一个指向这样数组的指针。因此:
int array[12][31];
int (*parray)[31];
parray=array;
这样,parray将指向array的第一个元素。
8、指针大小
char *pc;
sizeof(pc);
在32位机器中是4字节;
在64位机器中是8字节;
9、if(3 == i){...}
如此写法可以避免 将关系运算符写成赋值运算符,避免bug;
10、malloc动态申请的内容,释放后,将变量指向地址为空的地方会更加安全
int *pmal=(int *)malloc(100*sizeof(int));
...
...
...
if(pmal)
{
free(pmal);
pmal=NULL;
}