C中的宏最好只用于命名常量,并未一些适当的结构提供简洁的记法。宏名应该大写,这样便很容易与函数调用区分开来。千万不要用C预处理器来修改语言的基础结构,因为这样就不再是C语言了。
#define a(y) a_expanded(y)
a(x); 输出为 a_expanded(x);
而:#define a (y) a_expanded (y)
a(x); 则输出为 (y) a_expanded (y)(x)
函数声明中最好不要省略形参名,尽管编译器并不理睬形参的名称,但它们经常能向程序员传递一些有用的信息。
任何学习或使用C语言的人都应当使用ANSI C,而不是K&R C(1978年,C语言经典名著The C Programming Language,作者为Brian Kernighan和Dennis Ritchie)。尽管有ANSI C标准同时规定了一些编译器标准,但不同的编译器实现还是略有不同,需要参考相关编译器的说明文档。
有时候必须非常专注的阅读ANSI C标准才能找到某个问题的答案。例如下列代码:
foo(const char **p){}
main(int argc, char **argv)
{
foo(argv);
}
如果编译这段代码,编译器会发出一条警告信息:line 5: warning: argument is incompatible with prototype。 为什么实参char **argv与形参const char **p不相容呢?
标准6.3.2.2中规定:每个实参都应该具有自己的类型,这样它的值就可以赋值给它所对应的形参类型的对象(该对象的类型不能含有限定符)i.e.函数参数传递过程类似于赋值运算,即 形参=实参;标准中规定赋值语句6.3.16.1: 两个操作数都是指向有限定符或无限定符的相容类型的指针,左边的指针所指向的类型必须具有右边指针所指向类型的全部限定符。因此下面的语句是合法的,
char *cp;
const char *ccp;
cpp = cp;
左操作数是一个指向有const限定符的char的指针。
右操作数是一个指向没有限定符的char的指针。
char类型与char类型是相容的,左操作数所指向的类型具有右操作数所指向类型的限定符(无),再加上自身的限定符(const)。如果反过来就不能进行赋值。
const float *类型并不是一个有限定符的类型,它的类型是“指向一个具有const 限定符的float类型的指针”,即const并不是限定指针本身。因此char ** and const char **都是没有限定符的指针类型,但是它们所指向的类型不同,前者指向char *而后者指向const char*,因此它们是不相容的,所以不能进行参数传递。
const表示一个符号是只读的。通常用来限定函数的形参,这样函数就不会修改实参指针所指的数据,但是其他的函数却可能会修改它。例如:const int limit = 10;
const int * limitp = &limit;
int i = 27;
limitp = &i; 这段代码表示limitp是一个指向常量整型的指针,这个指针不能用于修改这个整型数,但这个指针可以想象不同的地址从而改变它自身的值,导致数值的改变。
自动类型转换:在执行算术运算时,操作数的类型如果不同,就会发生转换。数据类型一般朝着浮点精度更高、长度更长的方向转换,整数如果转换为signed不会丢失信息,就转换为signed,否则转换为unsigned。(ANSI标准)
例: int array[] = {23,24,25,26};
#define TOTAL (sizeof(array)/sizeof(array[0])) //此处使用了sizeof(array[0]),这样可以在改变数组类型时不用改变这条define语句
void main(int argc,char *argv[])
{
int d = -1;
if (d <= TOTAL-2){
printf("done!");
}
}
该代码段中的printf不会执行,因为TOTAL所定义的值为unsigned int类型(sizeof返回无符号数)。if语句在signed int和unsigned int之间测试相等性,所以d被升级为unsigned int,从而编程很大的正整数,条件表达式为假。如果要正确执行需要 if (d <= (int)TOTAL-2)进行强制类型转换。尽量不要在你的代码中使用无符号类型,以免增加不必要的复杂性。只有在使用位段和二进制掩码时,才可以用无符号数。应该在表达式中使用强制类型转换,使操作数均为有符号数或者无符号数,这样就不必由编译器来选择结果的类型。