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

《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--减去by = x / (*p);//这样就不会被编译器翻译为注释了a = -1;//a赋值
a -= -1;//a = a - (-1)
a -= 1;//a = a - 1

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


http://www.mrgr.cn/news/93783.html

相关文章:

  • 如何利用DeepSeek+OCR技术打造一款AI投标工具:自动解析招标文件并生成投标标书的工具?
  • 初识大模型——大语言模型 LLMBook 学习(一)
  • 利用pdf.js+百度翻译实现PDF翻译,创建中文PDF
  • 【UCB CS 61B SP24】Lecture 28 - Tries 学习笔记
  • Java多线程与高并发专题——什么是阻塞队列?
  • Linux中的TCP编程接口基本使用
  • 【开源界的Manus替代战:模块化设计 vs 跨平台实战 vs 全能开发,谁主沉浮?】
  • HTML5(Web前端开发笔记第一期)
  • Vue使用jsts,将wkt转为geojson
  • 微服务与消息队列RabbitMQ
  • Windows控制台函数:控制台窗口设置函数system(“mode con ...“)
  • 【Python】omegaconf 用法详解
  • 深入解析:(基础篇)Linux中KVM虚拟化技术
  • JU TPS研究笔记
  • 蓝桥云客 拔河
  • 【Go学习实战】03-2-博客查询及登录
  • rpc和proto
  • 【向量模型】 开源通用向量模型BGE (BAAI General Embedding)
  • NebulaGraph学习笔记-SessionPool之getSession
  • C语言_数据结构总结4:不带头结点的单链表