1、C语言是一种自由形式的语言,也就是说并没有规则规定什么地方可以书写语句,一行中可以出现多少条语句,什么地方应该留下空白以及应该出现多少空白等(预处理指令是一个例外,它是以行定位的)。唯一的规则就是相邻的标记之间必须出现一个或多个空白字符,不然他们可能被解释为单个标记。因此下列语句是等价的:
y=x+1;
y = x + 1;
y = x
+
1
2、ANSI C标准定义了三字母词,三字母词就是几个字符的序列,合起来表示另一个字符。三字母词使C环境可以在某些缺少一些必须字符集上实现。如:
??( [ ??
??) ] ??> } ??/ /
??! | ??' ^ ??- ~
两个问号开头再尾随一个字符一般不会i出现在其他形式的表达式中,所以把三字母词用这种形式来表示不会引起误解。例如:
代码:printf("??("); 输出:[
3、头文件“limits.h”说明了各种不同的整数类型的特点。如CHAR_BIT表示字符型的位数(至少8位),CHAR_MIN和CHAR_MAX定义了缺省字符类型的范围等。头文件“float.h”定义了名字FLT_MAX,DBL_MAX,LDBL_MAX分别表示float、double和long double所能存储的最大值。
4、由于不同机器对于缺省char类型要么是sighed,要么是unsigned,这意味着不同机器上char可能拥有不同的取值范围,所以只有当程序的所使用的char型变量的值位于sighed和unsigned char 的交集中时,这个程序才是可移植的(或者说获得最大程度的可移植性),例如,ASCII字符集中的字符都是位于这个范围之内的。如此类推。
5、字面值常量如:123、0173、0x7b等,在缺省情况下,字面值的类型就是可能的类型中最短但足以容纳整个值的类型。
6、ANSI C规定整型值相互之间大小的规则如下:
长整型至少应该和整型一样长,而整型至少应该和短整形一样长。
long double至少和double一样长,而double至少和float一样长。
7、signed、unsigned不能用于浮点类型。若在VS中用了类似unsigned double或signed double这样的类型声明,会有警告。同时看如下代码:
unsigned float f = -1.0;
float fl = -1.0;
直接将f和fl两者进行比较,两者不相等,只有将fl强转为unsigned float或将f强转为signed float(float不行)之后,两者才算相等。
8、指针声明:
int * b, c, d; 等效于int * b; int c; int d;
而:
int * b, * c, * d; 才等效于声明三个指针。
9、函数若没有声明返回类型,则默认返回类型为int,若没有参数,则参数表为void;
10、C语言没有布尔类型,所以像X>3这样的关系表达式的值是整型的0或1。可以通过如下方式引入自定义布尔类型:
#define TRUE 1
#define FALSE 0
11、goto语句使用的情况比较少,编程时候也应该尽量少用该语句,但是在一种情况下使用goto语句可能非常适合——就是跳出多层嵌套的循环。由于break语句只影响包围他的最内层循环,要想从深层嵌套的循环中退出只有使用一个办法,就是使用goto语句。如下所示:
while(conditon1)
{
while(conditon2)
{
while(conditon3)
{
if(some disaster)
goto quit;
}
}
}
quit: ;
12、EOF实际上是整型值-1,所以位数比字符型多,这也是getchar()函数返回一个整型值而不是字符值的原因。所以通常定义一个整型变量来存放函数读入的结果。
13、复合赋值操作符+=等与复制操作的唯一区别是,复合赋值操作符的左操作数只求值一次。如下所示:
a[f(x)] = a[f(x)] + 1;
a[f(x)] += 1;
后者f(x)只计算了一次,前者将计算两次。
14、强制类型转换符具有很高的优先级,所以把转换符放在一个表达式的前面只会改变表达式的第一个项目的类型,要强制想改变整个表达式的结果的类型,必须把整个表达式用括号括起来。
15、a++ = 10;
++a = 10;
前者合法,而后者非法,因为前者是将10赋值给a变量本身,而后者是将10赋值给a变量的拷贝(++a计算的结果),并不是变量本身。
16、&&和||操作符采用“短路求值”方法计算结果,对于&&,先对左操作符进行求值,如果它为假,就不会再对右操作数求值,因为整个表达式已经可以确定为假。||同理。
17、a > 5 ? b = 3 : b = 20; 可以写成: b = a > 3 ? 3 : 20; 当b是一个比较复杂的表达式时,可以简化代码。
18、逗号表达式的值是最后一个表达式的值。逗号表达式通过把多条语句合成一条语句也可以起到简化代码的作用。
19、b + 25 = a;非法,因为b + 25不是一个左值,是右值,虽然这个b+25的计算结果必然存储于内存中的某个地方,但是程序员无法知道该地址,也不能确保该地址不会马上存放别的值,所以是右值。左值意味着一个位置,右值意味着一个值,所以在使用右值的地方也可以使用左值,但是在需要左值的地方不能使用右值。
20、浮点数转换为整型值时,小数部分将被舍弃而不是四舍五入。
21、表达式的字符型和短整型操作数在执行运算之前会转换为普通的整型,称为整型提升。例如:
char a, b, c;
a = b + c;
b和c的值被提升为普通整型,然后再执行加法运算。加法运算的结果将被截断,再存储于a中。
22、一般算术转换是按照如下从下往上进行转换的:
long double
double
float
unsighed long int
long int
unsighed int
int
23、编译器可以自由决定以任何顺序对类似下面的表达式进行求值:
x + y + z
a * b * c
之所以允许编译器这样做,是因为y +z(b *c)的值可能可以从前面的表达式中获得,所以直接复用这个值比从新求值效率更高。但这样做的缺点如下:
x + y + 1
如果表达式x + y的结果大于整型所能够容纳的值,就会产生溢出,但是也有可能因为计算顺序的不同导致溢出地点可能在y + 1处。下面的表达式说明了一个相关的问题:
f() + g() + h()
尽管左边那个加法运算必须在右边那个加法运算之前执行,但对于各个函数调用的顺序,并没有规则加以限制。如果他们的执行循序具有副作用,比如执行一些IO任务或者修改全局变量,那么函数的调用顺序就会产生不同的结果。为了避免这种情况可以考虑使用临时变量,让每个函数调用都在单独的语句中执行:
temp = f();
temp += g();
temp += h();
24、假定变量a存储于位置100,可以通过*(int *)100 = 25;的方式给a赋值,但是不能通过*100 = 25;的方式赋值,因为间访操作符只能用于指针类型,所以要把整数100强制转换为int *类型。
25、字符串相关的操作函数的参数可以为空串,但是一般不允许为NULL,因为一旦参数为NULL,则说明程序在其他地方可能已经出错了。
26、当一个指针和一个整型值进行算术运算时,整数在执行加法运算之前始终会根据指针所指向的数据的类型进行相应的调整,如int * a; a+2;实际上a向后移动了2*sizeof(int)个字节。同理于减法,得到的是元素个数(对位于同一个数组中的元素地址进行相减才有意义,指针的关系运算也是如此)而不是字节数。
27、