C陷阱与缺陷

简介: C陷阱与缺陷

C陷阱与缺陷和高质量C编程这两本书主要是总结一些C编程人员常犯的一些错误,常遇到的一些语法陷阱,及不良的编程风格。无论是有多年经验的编程老手还是刚入门的新手,细细品读,都会有不少的收获和感悟。此篇博客是个人读完这两本书后的一些感悟及对一些内容的记录,方便日后翻阅。


词法陷阱

  • 多个操作字符组在一起的处理方法

编译器中负责将我们编译的命令符号进行分解的部分称为”词法分析器“,而词法分析器在工作运行时采用的处理策略为”贪心法“ 。什么意思呢?举个栗子,如这段语句a---b,编译器在运行时会识别为a-- - b而不是a - --b 也就是说编译器会先按从左到右的顺序读取一个字符,紧接着再读取第二个字符,然后判断这两个字符是否能够组成一个有实际意义的操作符,如果能组成,那么编译器还会读取第三个字符(如果存在第三的话)然后再进行同样的判断,直到读取的字符与前面所读取的字符的组合没有实际的意义了。我们再把视角拉回到a---b中,编译器会先读取” - “这个操作符,然后再读取一个” - “操作符,发现能够组成” -- “这个有实际意义的操作符,接着编译器再读取第三个” - “操作符,然后发现三个” - “操作符组成的字符没有实际意义,因此将” --- “拆分成了” -- “和” - “,最终语句的效果就是a-- - b ,这里需要注意的是,这是编译器在面临多个操作字符组在一起的处理办法。

  • 操作符之间的空格与括号的合理应用各个操作符之间的空格要根据想表达的意思谨慎添加,比如想表达是否等于的判断,应该写成==,而不是= =,= =这样的格式,严格的编译器是直接报错的。如 m = n/*p 这段语句的本意是n除以p指向的内容,然后把值赋给m,但编译器会将 /* 解读成注释的前半部分,不会再管后面的内容,如果写成m = n / *p 或者m = n / (*p)就能表达出想要表达的意思。
  • 不同的编译器采用的操作符的处理办法不尽相同,所以我们在编写程序时该加空格就加空格,该加括号就加括号,尽量把意思表达清楚,避免写出会产生歧义的代码,别舍不得用括号,不为难编译器,也不要为难自己。

    image.png整型数字运算需要注意的

  • 整型数字在运算时,避免为了使数字对齐美观而在数字面前加上 0 ,这样的话,编译器会将这串十进制数字识别为八进制数字,如果不知道这种情况,会导致得不到想要的结果而苦恼
//例如
int a = 123;
int b = 034;  //这里编译器会把34当作八进制数字来处理

如果在进行整数除法运算,且想得到具体的结果时,最好将其转化成浮点型,使用强制类型转换,或在某个数字后面加上" .0 "编译器会自动将其转换成浮点型

    float c = 0.0f;
    c = 1 / 4;
    printf("%f", c);//不可,c的结果为0.000000
    float c = 0.0f;
    c = (float)1 / 4;
    printf("%f", c);//可,c结果为0.250000
    float c = 0.0f;
    c = 1.0 / 4;
    printf("%f", c);//可,c结果为0.250000

char类型本质上是属于整型家族的,其在运算时,需要进行整形提升,为什么进行整型提升呢?

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度 一般就是int的字节长度,同时也是CPU的通用寄存器的长度。 因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长 度。 通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令 中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转 换为int或unsigned int,然后才能送入CPU去执行运算。

在存储时要进行截断,因为char只能存放8个二进制数

image.png字符与字符串的区别

由单引号引起的字符,本质上代表着一个整数,该整数值对应着编译器采用的字符集所对应的字符的序列值。由双引号引起的字符串,本质上是一个指针,指向一个无名数组起始字符的指针,该数组被双引号之间的字符以及\0所初始化。

避免在单引号的字符里放入多个字符,因为不同编译器的处理规则是不同的,有的取第一个字符,有的取最后一个字符,所产生的结果都是不确定的。也不要将单引号引起的字符赋值给char 类型的指针,这样是不行的,因为它的本质就是一个整数,但有的编译器不会报错的,这需要我们多加留意了。

char *p = 'a'; //这样不行,'a'不是一个指针;

//由于printf()函数的一些特性,采用不确定参数,原型为 int printf(const char *format[argument]...)

//导致一些编译器对该函数的参数的检查并不是很严格

//这就容易导致一个问题

printf('\n'),这种写法可能不会报错,这会导致难以预料的结果

image.png良好的编程习惯

为了避免产生一些不必要的错误,我们可以从编程习惯上逐步改善

int a = 3;
if ( a == 3 )     不妨将其写成if (3 == a)
{                 3是无法被赋值的,如果少些一个 = 编译器会报错的
 ......
}
//关键字与括号的接触中间最好有一个空格,以表明和函数的区别
if()      //建议写成if ()
while()   建议写成while ()  
//函数是紧贴括号的
    printf()
    scanf()
   // 不过现在一些高级的编译器会自动加空格的,还是很方便的;
 //函数在声明和定义时,最好将变量名补全 。
  void fun(int; int)//不好的风格
  void fun(int x, int y)//良好的风格




目录
相关文章
|
存储 Ubuntu C语言
Ubuntu 软件安装方法(入门必看)
Ubuntu 软件安装方法(入门必看)
Ubuntu 软件安装方法(入门必看)
|
Python
Python办公自动化:删除任意页数pdf页面
Python办公自动化:删除任意页数pdf页面
359 2
Python办公自动化:删除任意页数pdf页面
|
监控 NoSQL 安全
如何在 Redis 中正确使用多线程?
【10月更文挑战第16天】正确使用 Redis 多线程需要综合考虑多个因素,并且需要在实践中不断摸索和总结经验。通过合理的配置和运用,多线程可以为 Redis 带来性能上的提升,同时也要注意避免可能出现的问题,以保障系统的稳定和可靠运行。
353 2
|
7月前
|
Java 关系型数据库 MySQL
JVM深入原理(六)(二):双亲委派机制
自定义类加载器打破双亲委派机制的方法:复写ClassLoader中的loadClass方法常见问题:要加载的类名如果是以java.开头,则会抛出安全性异常加载自定义的类都会有一个共同的父类Object,需要在代码中交由父类加载器去加载自定义类加载器不手动指定parent会默认指定应用类加载两个自定义类加载器加载同一个类会被认为是两个对象,只有相同的类加载器+想通的类限定名才会被认为是一个对象。
263 0
|
缓存 算法 测试技术
性能测试的结果如何才算准确?
性能测试的结果如何才算准确?
404 57
|
11月前
|
人工智能 数据可视化 数据库
低代码平台:技术复杂性的系统简化
低代码平台通过模块化和自动化技术,简化了传统开发流程中的需求分析、代码开发、测试部署等环节,显著提高了开发效率和协作能力。其核心特性如“一键编程”、“快速迭代”降低了开发复杂度,提供了敏捷开发的能力,使企业能更快响应市场需求和技术变革。可视化开发、实时渲染、分布式协作支持及无缝部署等功能进一步优化了开发体验。平台内置的五大核心引擎(SQL、功能、模板、图表、切面)进行了系统性优化,提升了数据处理能力和开发灵活性。此外,低代码平台还融合了AI技术,提供了智能代码助手、自动优化和故障排查等功能,增强了开发效率和精度。
|
消息中间件 编解码 运维
阿里云 Serverless 异步任务处理系统在数据分析领域的应用
本文主要介绍异步任务处理系统中的数据分析,函数计算异步任务最佳实践-Kafka ETL,函数计算异步任务最佳实践-音视频处理等。
176250 349
|
12月前
|
弹性计算 网络协议 安全
下一代互联网IPv6规模部署和应用
本文介绍了IPv6在云计算场景下的规模部署与应用创新,强调其作为互联网演进的必然趋势及网络强国建设的基础支撑作用。文章从企业上云部署IPv6、云上IPv6网络底座构建、双栈方案全景图、专有云IPv6改造、政务云和金融客户的实践案例等方面展开讨论,详细阐述了IPv6在不同场景下的技术要求和服务能力。最后展望了IPv6与AI结合的未来发展方向,旨在推动IPv6的全面应用和技术创新。
|
设计模式 数据安全/隐私保护
责任链模式实战:在项目中的应用与技巧
在软件工程中,责任链模式(Chain of Responsibility)是一种对象行为型设计模式,它允许多个对象有机会处理请求,从而避免请求发送者与接收者之间的耦合。这种模式在处理复杂的业务逻辑和系统解耦方面非常有用。本文将探讨责任链模式的基本概念、应用场景以及在实际项目中的应用技巧。
307 4
|
数据可视化 JavaScript 前端开发
数据可视化进阶:D3.js在复杂数据可视化中的应用
【10月更文挑战第26天】数据可视化是将数据以图形、图表等形式呈现的过程,帮助我们理解数据和揭示趋势。D3.js(Data-Driven Documents)是一个基于JavaScript的库,使用HTML、SVG和CSS创建动态、交互式的数据可视化。它通过数据驱动文档的方式,将数据与DOM元素关联,提供高度的灵活性和定制性,适用于复杂数据的可视化任务。 示例代码展示了如何使用D3.js创建一个简单的柱状图,展示了其基本用法。D3.js的链式调用和回调函数机制使代码简洁易懂,支持复杂的布局和交互逻辑。
418 3