(1)词法分析中的贪心法:如果(编译器的)输入流截止至某个字符之前都已经被分解为一个个符号,那么下一个符号将包括从该字符之后可能组成一个符号的最长字符串。
——编译器将程序分解成符号的方法是,从左到右一个字符一个字符的读入,如果该字符可能组成一个符号,则读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分;如果可能则继续读入下一个字符,重复上述判断,直到读入的字符组成的字符串已不再可能组成一个有意义的符号。
编程的书写要注意空格,以下举例来说明:
1. a---b 与 表达式 a-- - b的含义相同,而与 a - --b的含义不同。
2. 如果/之后紧跟着*,则无论上下文如何,这两个字符都将被当做一个符号/*,表示一段注释的开始。
假设某代码中,有一个指针p,有一个变量x,代码中的本意是用x除以p所指向的值,把所得的商赋给变量y:
如果写成 y = x/*p;
如果一个整型常量的第一个字符是数字0,那么该常量将被视作八进制数。
用单引号引起的一个字符代表的是一个整数,整数值对应于该字符在编译器采用的字符集中的序列值。因此,对于采用ASCII字符集的编译器而言,'a'的含义是97.
用双引号引起的字符串,代表的却是一个指向 无名数组 起始字符的指针,该数组被双引号之间的字符以及一个额外的二进制字符'\0'初始化。
printf("Hello World\n");
与
char hello[] = {'H', 'e', 'l','l','o', 'W', 'o', 'r', 'l', 'd', '\n', 0};
是等效的。
也因为单引号引起的内容代表字符,双引号引起的内容代表无名数组,故错误使用时,有可能会出现难以预料的错误。如,
1. char *slash = '/'; //出错,因为'/'不是指针
char* p , *q;
p = "xyz";
首先:出错的地方在于,循环条件中的i<N,写成了i<=N。最后一次赋值,a[10] = 0怎么会出现这么严重的情况呢?原因就在于数组a之后的这片内存它到底存放的是什么。这里其实跟编译器分配内存的方式有关,如果编译器是按照内存地址递减的方式来给变量分配内存的,那么数组a之后的一个字(word)实际上是分配给了整型变量i。此时,当循环计数器i的值为10时,循环体内将并不存在的a[10]设置为0,实际上修改的却是计数器i的值,这样就陷入了死循环。
——两道有趣的思维题(边界问题的代表):
1.100英尺长的围栏每隔10英尺需要一根支撑用的栏杆,一共需要多少根栏杆?
2.假定整数x满足边界条件x>=16且x<=37,那么这个范围内的x的取值个数有多少?
(7)整数的算术运算和整数的溢出
1.如果算术运算符的一个操作数是有符号整数,另一个是无符号整数,那么有符号数会被转换成无符号数。
故:只有当两个操作数都是有符号整数时,“溢出”才会发生,而且溢出的结果是未定义的。当一个运算的结果发生“溢出”时,做出任何假设都是不安全的。
2.假定a和b是两个非负整型变量,如何检查a+b是否会“溢出”?以下方式判断溢出怎么样?
if(a+b < 0)
printf("溢出\n");
else
printf("没有溢出\n");
分析:这种方法不可取,当a+b发生溢出时,所有关于结果如何的假设都不再可靠。例如,某些机器上,加法运算将设置一个内部寄存器为四种状态:正、负、零、溢出。在这种机器上,C编译器完全没有理由这样来实现上面的例子,即a+b溢出的时候,这个寄存器的状态是溢出,而不是为负。这种时候if语句就会失败。
检查a+b是否溢出的推荐方式(先转换成无符号整数):
if((unsigned)a+(unsigned)b > INT_MAX)
printf("溢出\n");
else
printf("没有溢出\n");
这里的INT_MAX是一个已定义的常量,代表可能的最大整数值。表C在<limits.h>头文件中定义了INT_MAX;如果是在其它C语言实现上,需要的时候,程序员可以自己定义INT_MAX的值。
——编译器将程序分解成符号的方法是,从左到右一个字符一个字符的读入,如果该字符可能组成一个符号,则读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分;如果可能则继续读入下一个字符,重复上述判断,直到读入的字符组成的字符串已不再可能组成一个有意义的符号。
总结:每一个符号都应该包含尽可能多的字符。
编程的书写要注意空格,以下举例来说明:
1. a---b 与 表达式 a-- - b的含义相同,而与 a - --b的含义不同。
2. 如果/之后紧跟着*,则无论上下文如何,这两个字符都将被当做一个符号/*,表示一段注释的开始。
假设某代码中,有一个指针p,有一个变量x,代码中的本意是用x除以p所指向的值,把所得的商赋给变量y:
如果写成 y = x/*p;
则/*必定被当做一段注释的开始,编译器会不断地读入字符直到*/出现为止。也就是说,编译器不会顾及程序员编写代码时程序员的思维逻辑。如果将程序改成 y = x / *p 或 y = x/(*p)则程序不会出错,这样写程序,其规范性也显得更好。
如果一个整型常量的第一个字符是数字0,那么该常量将被视作八进制数。
如 10 与 010 的含义是截然不同的。
用单引号引起的一个字符代表的是一个整数,整数值对应于该字符在编译器采用的字符集中的序列值。因此,对于采用ASCII字符集的编译器而言,'a'的含义是97.
用双引号引起的字符串,代表的却是一个指向 无名数组 起始字符的指针,该数组被双引号之间的字符以及一个额外的二进制字符'\0'初始化。
printf("Hello World\n");
与
char hello[] = {'H', 'e', 'l','l','o', 'W', 'o', 'r', 'l', 'd', '\n', 0};
是等效的。
也因为单引号引起的内容代表字符,双引号引起的内容代表无名数组,故错误使用时,有可能会出现难以预料的错误。如,
1. char *slash = '/'; //出错,因为'/'不是指针
2.printf('\n'); //出错,或警告。程序运行将出现难以预料的错误。
char* p , *q;
p = "xyz";
始终记得“xyz”代表的是由'x','y','z','\0'四个元素组成的无名数组,而整个字符串就代表的这个数组的首地址,p = "xyz"就是让p指向字符串"xyz"的首地址。
(6)以下程序出现死循环的原因:
#include <stdio.h>
#define N 10
int main(){
int i = 0;
int a[N];
for(i = 0; i <= N; i++)
a[i] = 0;
for(i = 0; i <= N; i++)
printf("%d\n",a[i]);
return 0;
}
首先:出错的地方在于,循环条件中的i<N,写成了i<=N。最后一次赋值,a[10] = 0怎么会出现这么严重的情况呢?原因就在于数组a之后的这片内存它到底存放的是什么。这里其实跟编译器分配内存的方式有关,如果编译器是按照内存地址递减的方式来给变量分配内存的,那么数组a之后的一个字(word)实际上是分配给了整型变量i。此时,当循环计数器i的值为10时,循环体内将并不存在的a[10]设置为0,实际上修改的却是计数器i的值,这样就陷入了死循环。
——两道有趣的思维题(边界问题的代表):
1.100英尺长的围栏每隔10英尺需要一根支撑用的栏杆,一共需要多少根栏杆?
2.假定整数x满足边界条件x>=16且x<=37,那么这个范围内的x的取值个数有多少?
(7)整数的算术运算和整数的溢出
1.如果算术运算符的一个操作数是有符号整数,另一个是无符号整数,那么有符号数会被转换成无符号数。
故:只有当两个操作数都是有符号整数时,“溢出”才会发生,而且溢出的结果是未定义的。当一个运算的结果发生“溢出”时,做出任何假设都是不安全的。
2.假定a和b是两个非负整型变量,如何检查a+b是否会“溢出”?以下方式判断溢出怎么样?
if(a+b < 0)
printf("溢出\n");
else
printf("没有溢出\n");
分析:这种方法不可取,当a+b发生溢出时,所有关于结果如何的假设都不再可靠。例如,某些机器上,加法运算将设置一个内部寄存器为四种状态:正、负、零、溢出。在这种机器上,C编译器完全没有理由这样来实现上面的例子,即a+b溢出的时候,这个寄存器的状态是溢出,而不是为负。这种时候if语句就会失败。
检查a+b是否溢出的推荐方式(先转换成无符号整数):
if((unsigned)a+(unsigned)b > INT_MAX)
printf("溢出\n");
else
printf("没有溢出\n");
这里的INT_MAX是一个已定义的常量,代表可能的最大整数值。表C在<limits.h>头文件中定义了INT_MAX;如果是在其它C语言实现上,需要的时候,程序员可以自己定义INT_MAX的值。