承德网站建设设计,淄博公司做网站,泉州企业网站建设公司,做公众号需要网站导言#xff1a;
由于一个程序错误可以从不同层面采用不同方式进行考察#xff0c;而根据程序错误与考察程序的方式之间的相关性#xff0c;可以将程序错误进行划分为各种陷阱与缺陷#xff1a; ①.词法“陷阱” ②.语法“陷阱” ③.语义“陷阱” ④.连接问题 ⑤.库函数问…导言
由于一个程序错误可以从不同层面采用不同方式进行考察而根据程序错误与考察程序的方式之间的相关性可以将程序错误进行划分为各种陷阱与缺陷 ①.词法“陷阱” ②.语法“陷阱” ③.语义“陷阱” ④.连接问题 ⑤.库函数问题 ⑥.预处理器问题 ⑦.可移植性缺陷 我们知道程序是由符号序列所组成的。本篇主要考察在程序被词法分析器分解成各个符号的过程中可能出现的问题也就是词法“陷阱”。导言1.不同于1.1 案例11.2 案例21.3 启示1.4 案例32.和 |不同于和 ||3.词法分析中的“贪心法”3.1 读取规则---“贪心法”3.2 注意3.3 “贪心法”读取案例14.整形常量5.字符和字符串5.1 字符5.2 字符串6.练习题6.1 练习题①6.2 练习题②1.不同于
符号 作为赋值运算符 符号作为比较运算符 由于在C语言中赋值操作相对于比较出现更加频繁所以将字符较少的符号赋予更常用的含义—赋值操作。 不过这种便利的使用可能导致一个潜在的问题当程序员本想使用比较运算符却使用了赋值运算符。
1.1 案例1 if(xy)
break;该句的本意是想检查x是否等于y但实际上却是将y的值赋给了x然后检查表达式结果是否为0。
1.2 案例2
再比如下面这个例子 例子中循环语句本想跳过空格符制表符和换行号 while(i ||i\t||i\n)igetchar();但是却把比较运算符误写成赋值运算符因为赋值运算符的优先级是低于逻辑运算符||所以语句先将’ ‘赋给了i然后发先i结果不为0’ ASCII码值是32根据短路求值原则左边为真则右边不再进行此语句也就是真因此循环将一值进行下去直到整个文件结束。或者变成一个死循环。
1.3 启示
某些C编译器在发现形如ab的表达式出现在循环语句的条件判断部分时会给出警告以提醒程序员。当确实需要对变量进行赋值并检查该变量的新值是否为0时为了避免该类编译器的警告我们应该进行显式地比较比如
if(xy)
{Function
}应该写成
if((xy)!0)
{Function();
}这样的写法就使得代码的意图一目了然。
1.4 案例3
前面一直谈的是将比较运算符误写成赋值运算符另一方面如果把赋值运算符误写成比较运算符也会造成混淆。
if ((i fun(a, b)) 0)error();本语句的原本意思是如果fun函数执行成功则返回0执行失败则返回-1或者正数然后再将返回值赋给i通过比较i的大小来确定是否fun函数执行成功。但实际上是fun函数的返回值与i进行比较结果只有真与假也就是0或1永远不可能小于0所以函数error没有机会调用。也就不知道fun函数调用是否失败。
2.和 |不同于和 ||
3.词法分析中的“贪心法”
C语言有许多符号比如只有一个字符长的单字符符号,和多个字符长的多字字符。比如 和 /*。 当C编译器读取符号时是如何读取的呢
比如读入一个字符’/‘后又跟了一个字符’ *那编译器是将其作为两个分别的符号对待还是合起来作为一个符号对待
3.1 读取规则—“贪心法”
每个符号应该包含尽可能多的字符。 也就是说编译器将程序分解成符号的方法是 从左到右一个字符一个字符地读入如果该字符可能组成一个符号那么再读入下一个字符判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分如果可能继续读入下一个字符重复上述判断直到读入的字符组成字符串不再可能组成一个有有意义的符号。 这个处理策略就被称为“贪心法”。
3.2 注意
需要注意的是除了字符串与字符常量符号的中间不能嵌有空白空格符制表符和换行符。 比如
a---b 与表达式
a-- -b的含义相同但与
a- --b的含义完全不同。3.3 “贪心法”读取案例1
本代码想表达的意思是x先除以指针p指向的数然后再将所得的商赋给y.
yx / *p;// *p指向的是被除数而实际上/*被编译器理解为一段注释的开始编译器将不断的读入字符直到另一个 * /出现为止。 也就是说该代码直接将x的值赋给了y根本不会顾及后面出现的p。
而正确的表达意思应该是这样
yx/(*p);//*p指的是被除数4.整形常量
我们知道一个整形常量第一个字符是数字0则这个常量代表的是八进制位数。 因此10与010的意思不一样。 而有的C编译器会把89也作为八进制数字来处理感觉很奇怪所以ANSI C标准禁止了这种用法以免混乱。 不过有时候在上下文中为了格式对齐可能会将十进制数写成八进制数。比如
035
044
123//只是单词的使格式对齐没有多的意思5.字符和字符串
C语言中字符与字符串的区别在于单引号与双引号的使用在某些情况下如果把两者弄混编译器并不会监测报错从而运行时产生难以预料的结果。我们先来看看字符与字符串的区别
5.1 字符
用单引号引起的一个字符实际上代表一个整数整数对应着ASCII码值的字符序列值比如’a‘的含义与0141八进制或者97十进制严格一致。
整数一般为16位或32位的存储空间可以容纳多个字符一般位8位因此有的C编译器允许在一个字符常量以及字符串常量中包含多个字符。也就是用‘yes’代替“yes”不会被该编译器监测到。“yes”代表的意思是依次包含‘y’‘e’‘s’以及空字符‘\0’的4个练习内存单元的首地址。而‘yes’的含义并没有准确的进行定义但大多数编译器理解为“一个整数值”
5.2 字符串
用双引号引起的字符串代表的是一个指向无名数组起始字符的指针该数组被双引号之间的字符以及一个额外的二进制为0的字符’0\‘初始化。
#include stdio.h
int main()
{printf(xiao tao lai lo\n);char ch[100] xiao tao lai lo;//ch是数组名数组名是首元素的地址也就是首字符的地址。printf(ch);//ch里存放着首元素的地址}这说明这两个打印效果是一样的所以一个字符串代表着一个指向无名数组起始字符的指针。可以用一个指针来存放字符串
#include stdio.h
int main()
{
char* p xiao tao lai lo;//指针p里面存放的就是字符串的首字符地址。printf(p);
} 6.练习题
6.1 练习题① 答案 根据“贪心法”规则在编译器读入之前就已经将- -作为一个符号而后面的不能和前面的组成一个符号。
6.2 练习题② 答案 这个代码唯一有意义的解析是这样
a b但是根据“贪心法”规则上面代码应该被分解为
a b这个式子从语法上来说是不对的它应该写成
((a))b但是a的结果不能作为左值因此编译器不会接收a作为后面的运算符的操作数该代码没有意义。
…………………………………………………………………………………………………………………… ………………………………………………………………………加油…………………………………………………………