当前位置: 首页 > article >正文

《C陷阱与缺陷》读书笔记(一)

目录

一、= 不同于 == :

二、& 和 | 不同于 && 和 || :

三、词法分析中的贪心法:


一、= 不同于 == :

        对于C语言中的“=”和“==”,它们虽然只相差了一个等号,但是含义确实千差万别。“=”在C语言中是非常常用的操作符,它的含义是将右边的值赋值给左边的值,这个过程要求左值是可以更改的。而“==”的含义是比较两个数字(或者字符)是否相等,通常用于逻辑判断语句中。

        而我们虽然知道它们的含义,但还是有可能在编写代码时将它们写错的,比如书中给出了几个例子:

if (x = y)
    break;

//注意,C语言不允许if语句中使用break关键字
//所以我推测这段代码应该是循环语句中的一部分

        上面代码本意是判断x和y是否相等,如果它们相等,就跳出循环,但是却将“==”误写成了“=”,这样一来if条件判断语句永远为真(赋值语句的结果恒为真),所以就会跳出循环,没有判断x和y相等这一过程。那么想避免这种情况的发生,其实也是可以的,我们可以利用赋值操作的左值必须可更改,在判断是否相等时将左值变成不可更改的操作数,比如下面的代码:

const int x = 0;
int y = 0;
//我将上面代码的x和y进行了定义,并且将x设定成const修饰的常变量

//如果这样写,程序就会报错,因为x不可更改
if (x = y)
    break;

//当我们看到报错信息,就知道是这里写错了,所以就会及时改正
if (x == y)
    break;

//同时,再进行与常量相比较的操作中,也可以采用这种方法
10 = x;//这样写就会报错
10 == x;

下面看书中的下一个例子:

while (c = ' ' || c == '\t' || c == '\n')
    c = getc(f);

        这个代码中误将“==”写成了“=”,导致最开始是为c赋值为空字符串,那这行语句恒为真,所以“||”运算符有一个短路现象,它就不会向下执行,也就不会判断c是否等于“\t”和“\n”了。同时既然“||”不会向下判断,那while循环的条件也恒为真,该循环很可能是死循环。

        对于这种误写,可以采用上面的方法,将字符放在左值,这样遇到“=”时就会报错。或者还可以将“==”判断改成“!=”判断,之后逻辑上取反即可,这样写也未尝不可,只是有时候逻辑上会显得有点绕,比如下面的代码:

//本来是如果x和y相等就进入语句,但是为了防止==写成=,可以采用!=替代==
if (x == y)
    break;

//使用!=替代==,这样当x与y不相等的逻辑反(实际上还是x与y相等)就进入语句
if (!(x != y))
    break;

//上面的while循环也可以这样改写,并且我习惯将每个逻辑判断部分加上括号
while (!(c != ' ') || !(c != '\t') || !(c != '\n'))
    c = getc(f);

        虽然上面代码可以解决将“==”写成“=”的情况,但是我们在编程时还要多多细心才好,万一漏写了逻辑反符号呢对吧。另外,如果是错写了函数名或变量名还好,编译器会报错,那要是错写或漏写操作符的话,可能会导致各种问题,比如程序死循环、栈溢出、越界访问,这都不太致命,最致命之一的是运行结果与期望结果不符。

        另外,像我上面那样多加括号也是一种编程的好习惯,这样不仅在观感上清晰明了,也可以避免因操作符优先级而带来的一系列问题。

下面看看将“=”误写成“==”的代码:

if ((filedesc == open(argv[i], 0)) < 0)
    error();

        这段代码本意是将函数的返回值赋给变量filedesc,可结果却是比较filedesc与open()函数的值,之后使用比较结果与0进行比较。open()函数如果执行成功将返回0或者正数执行失败将返回-1,这样通过open()函数的返回值与0比较(实际是先将返回值赋值给filedesc之后再进行比较),当open()函数执行失败后会利用error()函数打印错误信息。

        那对于这个代码该如何处理呢?我想到的办法是将filedesc初始化为-1(我发现书中的一个不太好的习惯就是不进行初始化),并且不和0比较,代码如下:

const int filedesc = -1;

if (filedesc == open(argv[i], 0))
    error();

        我们看open()函数的返回值无非是-1和0(或正数),如果返回-1,刚好与filedesc相等,进入if语句,否则不进入。当filedesc定义为const的常变量时,即使误写成了“=”,程序也会报错提醒。当然,原来的代码本意应该是使用filedesc去保存open()函数返回的值,我该的代码直接判断open()是否执行正确了,所以还不如写成下面这样:

const int filedesc = open(argv[i], 0);

if (!filedesc)
    error();

        注意啊,这样写代码有一个好处就是变量filedesc直接初始化为open()函数的返回值,因此如果误将“=”写成“==”,程序就会报错

二、& 和 | 不同于 && 和 || :

        对于C语言中的位操作符和逻辑操作符,这其实很好区分。“&”可以翻译为“and”,“|”可以翻译为“or”,所以它们表示的是二进制的按位与和按位或。“&&”和“||”是逻辑与和逻辑或,与“&”和“|”没有关系。当然如果考虑到再进行逻辑判断时,将“&&”和“||”误写成“&”和“|”,这样的话可能会得到一个比较奇怪的结果,我也想不到了如果误写后的改正方法。

三、词法分析中的贪心法:

对于这一部分呢,书中的阐述还是一些使用操作符时的歧义问题,比如看下面代码:

int a = 0, b = 0, x = 0, y = 0, p = 0;

//如果写成这样a---b,就会产生歧义
a - --b;
a-- - b;

y = x /* p;//C语言中的/*被规定为注释的开始
y = x / *p;//这个代码是x除以p的解引用

a = -1;//a赋值
a =- 1;//这行语句在古老的编译器上表示a -= 1,现在已经不支持这种语法了

        对于上面产生的一系列问题,我给出的解决办法是不管操作符的优先级和结合性,按照自己的想法加上括号即可,代码如下:

int a = 0, b = 0, x = 0, y = 0, p = 0;

a - (--b);//a减去--b
(a--) - b;//a--减去b

y = x / (*p);//这样就不会被编译器翻译为注释了

a = -1;//a赋值
a -= -1;//a = a - (-1)
a -= 1;//a = a - 1

        注:再次强调,一些良好的编程习惯有多写有意义的注释、多加小括号、对代码进行适量的缩进(一般是4个空格)和空行、创建变量最好要进行初始化。


http://www.kler.cn/a/578522.html

相关文章:

  • Sqli-labs 1-20
  • 使用 vxe-table 导出 excel,支持带数值、货币、图片等带格式导出
  • 开放充电点协议(OCPP)技术解析:架构演进与通信机制 - 慧知开源充电桩平台
  • Deeplabv3+改进2:添加A2Attention注意力机制|有效涨点
  • VUE2脚手架的下载与安装
  • 基于SpringBoot+MyBatis+MySQL+Vue的智能家居管理系统设计与实现(附源码+数据库+毕业论文)
  • Django与视图
  • 英文字体:现代复古美学精致细节浓缩式衬线排版logo标题艺术字体 La Luxes Serif
  • R语言和RStudio安装
  • Xenium数据分析 | 数据预处理、单细胞降维聚类、细胞类型定义
  • 《几何原本》命题I.24
  • VBA 根据日期字符串 返回日期格式(只能识别部分常用格式)
  • 解锁DeepSpeek-R1大模型微调:从训练到部署,打造定制化AI会话系统
  • IO多路复用实现并发服务器
  • 三、滑动窗口——9. 找到字符串中所有字母异位词
  • c++20 在 <chrono> 中的 日历 和 时区 库
  • cmd中有cl但是conda虚拟环境没用cl
  • 【Recon】Git源代码泄露题目解题方法
  • Java在word中动态增加表格行并写入数据
  • 中级网络工程师面试题参考示例(4)