第一章 词法陷阱
笔记本:《C陷阱与缺陷》
创建时间:2018/4/23 22:06:21 更新时间:2018/4/24 08:36:21
作者:刘依阳
导读
有的人说,学习一门语言,就要知道他的底层原理,动手能力不着急,有的人说,学习一门语言最重要的是要会动手,要有项目实战经验,要知道一些API和框架,要我说,这二者缺一不可。
有的人大一C语言C++学得特别好,善于从上帝视角看待C++程序脚本,沉迷于命令行中字符串的输入输出,编码能力得到了很大的提升。但人的精力和耐力是有限的,没有需求支撑、没有项目支撑,迟早有一天我们会因为学习压力、生活压力、工作压力而被迫抛弃我们最爱的底层原理和OnlineJudge,在这之后,之前学得再扎实的语言语法也会渐渐被我们忘掉大半…
还有的人,沉迷于做项目,项目很大很复杂,能做出来确实很了不起,但其实这之中有大量的重复编码,我们自以为学了很多,其实也只是做了一个苦力活。如果我们只是会做项目,只是知道怎么写,而不知道为什么这么写,不知道项目里每一个类底层都干了些啥,编译器和运行环境为我们做了些啥,那我们写出来的只会是死代码,遇到一个新的问题,自己仍然不会用底层知识去理解去创造自己解决方案,这样的码农迟早要被飞速进步的互联网行业所淘汰,与培训机构出来的 “两年经验” 无异…
因为自己以前走过弯路,所以开始学习之前,还是要拿这段话来提醒一下自己…
进入正题:
有的程序虽然简单,但如果我们不注意,就很容易发生不可预料的错误,即使一些很有经验的程序员,也常常容易忽视这些问题。例如下面这段代码:
#include<iostream>
#define N 5
using namespace std;
main(){
int i;
int a[N];
for(int i=0; i<=N; i++)
cout<<(a[i]=0)<<endl;
}
乍一看只是FOR循环中的 “判定表达式” 多了一个等号,在Java中这将产生数组越界异常 “java.lang.ArrayIndexOutOfBoundsException” ,而在C语言中则没有这类运行时异常,程序照常执行,但这里发生了死循环。
其实这正是C语言指针的一个弊端,指针作为C语言中强大的工具,我们能通过它直接对某一内存地址中的值进行读写,在这里,变量 i 的内存地址恰好分配到了 a[9] 的下一个内存地址 a[10] ,每一次 i=10 时进入循环后,将执行 i=a[10]=0 ,i 将被再次置为0,这就造成了我们看到的死循环…
1 词法分析中的“贪心法”
- 相同的字符在不同的上下文中含义是不一样的,我们都学过编译原理,词法分析自然也不会陌生,术语 “符号” (token) 指的是程序的一个基本组成单元,其作用相当于一个单词,在C语言中,同一个单词 (token) 通常是无二义性的。
- 每一个符号应该包含尽可能多的字符,也就是说,编译器将程序分解成符号的 方法是,从左到右一个字符一个字符地读入,如果该字符可能组成一个符号,那么再读入下一个字符。需要注意的是,除了字符串与字符常量,符号的中间不能嵌有空白(空白符、制表符和换行符)。例如,==是单个符号,而= =则是两个符号(注意其中的空格符)。
例如:
运行结果如下:
2 =不同于==
- C语言中 “=” 是赋值运算符 “==” 是关系运算符,用于两个数进行比较…,例:
#include<iostream>
using namespace std;
int main(){
int x,y=5;
cout<<(x=y)<<(x==y)<<x<<y<<endl;
//尽量多用括号、空格、转义符等明确语义
cout<<(x==y)<<endl;
return 0;
}
程序的输出你想不到:
3 &和|不同于&&和||
&:按位与,优先级高于&&,也可作为逻辑与,作为逻辑与时没有短路机制...
|:按位或,优先级高于||,也可作为逻辑或,作为逻辑或时没有短路机制...
&&:逻辑与,短路机制...,返回布尔值(0或1)
||:逻辑或,短路机制...,返回布尔值(0或1)
示例:
#include<iostream>
using namespace std;
int main(){
int x,y=5;
//&:按位与,优先级高于&&
//|:按位或,优先级高于||
cout<<(x&y)<<" "<<(x|y)<<" "<<(!x)<<" "<<(x^y)<<" "<<(x>y)<<" "<<(x>>y)<<endl;//C语言没有>>>
//&&:逻辑与
//||:逻辑或
x=y=5;
cout<<(x==5&&y!=5)<<(x==5||y!=5)<<endl;
//&:也可作为逻辑与,作为逻辑与时没有短路机制...
//&&:逻辑与,短路机制...,返回布尔值(0或1)
//注意以下两种的区别
x=y=5;
cout<<(x!=5&x++==5)<<x;
cout<<(x!=6&&x++==6)<<x<<endl;
x=y=5;
cout<<(x!=5&x++==5)<<x<<(x!=6&&x++==6)<<x<<endl;
//|:也可作为