《C陷阱与缺陷》学习笔记

本文是作者刘依阳对《C陷阱与缺陷》的学习笔记,详细探讨了C语言中的词法陷阱、语法陷阱、语义陷阱、连接、库函数和预处理器等方面的问题。通过实例分析,解释了如"="与"=="的区别、"&"和"&&"、"|"和"||"的不同、以及预处理器宏的使用注意事项等常见陷阱,旨在帮助读者更好地理解和避免C语言编程中的常见错误。
摘要由CSDN通过智能技术生成

第一章 词法陷阱

笔记本:《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]=0i 将被再次置为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;

    //|:也可作为
  • 12
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值