C语言精华记录——捌(预处理、程序调试、编程风格)

C语言(深度解剖版) 底层逻辑改善程序的方法--开发人员使用 阅读详情

捌

预处理

C预处理器是一种简单的宏处理器。它在编译器读取源程序之前对C程序的源文本进行处理。预处理器一般从源文件中删除所有的预处理器命令行,并在源文件中执行这些预处理命令所指定的转换操作。

【宏只是进行简单的文本替换】

续行:

所有的源文件行(包括预处理器命令行)都可以在行末加个反斜杠( \ )进行续行。这个操作发生在对预处理器命令进行扫描之前。

【注意:续行符反斜杠之后不能有任何字符,尤其注意检查不能有空格等空白符。】

 

普通宏定义:

#define 命令有两种形式,取决于被定义的宏名后面是不是紧随一个左括号。若没有左括号,则为无参宏定义。

无参宏定义常用于:

1、在程序中引入名称常量。这样,可以在一个地方编写,然后通过名称在其它地方被引用,这样,以后修改这个数字就非常方便了。

2、改变外部定义的函数名或变量名。(有些外部函数的函数名过于简短或是与当前程序的命名风格不符,我们可以用宏定义一个新的函数名来代替它)

例:#define  error_handler  eh73

//我们用一个更具描述性的函数名error_handler来表示外部函数eh73

 

带参数的宏:

左括号必须紧随宏名之后,中间不能有空格。如果宏名和左括号之间被一个空格所分隔,则这个宏被定义为不接受任何参数,并且宏体从左括号开始。

注意:

1、为了保证宏展开的正确性,应该给每个宏参数加上括号,且给整个表达式也加上括号。

(多余的括号保证了复杂的实际参数不会被编译器错误的解释)

2、使用类似函数的宏,可能存在一些陷阱。我们在调用宏函数时,会习惯地加一个分号,而额外的分号可能引发错误。

例:#define  SWAP(type, x, y)  { type _temp=x; x=y; y=_temp; }

若 if( x > y) SWAP(int , x, y) ;

       elsex = y ;

//这将产生错误,宏展开后有一个多余的分号,将导致else悬空。

★为了避免这个问题,可以把宏函数体定义为一条do-while语句,后者可以接受在末尾添加分号。

#define SWAP(type, x, y)  \

do { type _temp=x; x=y; y=_temp; } while(0)

3、宏参数的副作用。(当宏参数含++、--操作符时一定要小心)

例:#define SQUARE(x)  ((x)*(x))

若 b =SQUARE(a++) ;       //则结果是未定义的,因为(a++)*(a++)的行为取决于编译器。

真正的函数调用不会出现这样的问题,真正的函数调用是先计算参数值,然后再调用函数。而宏函数,只是简单的文本替换。

 

【宏是与类型无关的,即其可以用类型做参数。故:宏有时可以完成无法用函数实现的任务】

例:#defineMY_MALLOC( n, type )  ((type*)malloc((n)*sizeof(type)))

 

取消宏定义:

# undef命令可以取消定义一个名称为宏:undef  name

 

条件编译

条件编译指令允许预处理器根据一个经过计算所得出的条件,来选择不同的语句参加编译。

#if  常量表达式

       文本行组1

#else

       文本行组2

#endif

常量表达式包括整数常量以及所有的整数算术、关系、位和逻辑操作符。

如果它的值不是0,则“文本行组1”则被编译器进行编译,而“文本行组2”则被丢弃。

defined操作符

defined 操作符只能在#if和#elif表达式中使用,而不能用于别处。

形式:definedname 或 defined(name)

#if defined( VAX )  可等同于 #ifdef VAX

但defined的使用更加灵活一些:

例:#ifdefined(VAX) && !defined(UNIX) && debugging

 

预定义的宏

标准C的预处理器定义了一些宏,这些宏的名称都是以两个下划线字符开始和结束的。程序员不能取消这些预定义宏的定义或对它们进行重新定义。

几个常用的预定义宏:

__LINE__             当前源程序行的行号,用十进制整数常量表示

__FILE__              当前源文件的名称,用字符串常量表示

__DATA__             编译时的日期,用“Mmm dd yyyy”形式的字符串常量表示

__TIME__             编译时的时间,用“hh:mm:ss”形式的字符串常量表示。

 

程序调试

一、使用断点和单步执行

详情请参阅具体的IDE使用说明

 

二、条件编译

#ifdef DEBUG

       printf(“File:%s line:%d, x=%d, y=%d”, __FILE__, __LINE__, x, y ) ;

#endif

如果要编译它,只要使用#defineDEBUG 即可,如果要忽略它,注释掉即可。

 

C99引入了一个预定义标识符:__func__

这个标识符可以由调试工具使用,打印出外层函数的名称。

例:if(failed)  printf(“Function %s failed \n”, __func__) ;

 

三、使用断言<assert.h>

断言就是声明某种东西应该为真。(预测某个值为多少,符合条件则继续,否则中止程序)

void assert( int express ) ;

当它被执行时,对表达式参数进行测试。

如果它的值为假(零),它就向标准错误打印一条诊断信息并中止程序。

否则它不打印任何东西,程序继续执行。

例:assert(value != NULL ) ;

//如果它接受了一个NULL参数,则打印类似:assertfailed :value != NULL.file.c line 273

【注意:断言只是在测试阶段,防御性地测试某个变量值的方法,不要再断言中写一些会对程序造成影响的表达式。因为在release版编译器会删除断言,若断言中的表达式对程序有影响,可能会产生错误!】

删除断言:

当程序被完整地测试完毕之后,在源文件的头文件assert.h被包含之前,增加定义:

#define NDEBUG

当NDEBUG被定以后,预处理器将会丢弃所有断言。

 

 

编程风格:

以下内容摘自《代码大全》

变量命名:

该名字要完全、准确地表述出该变量所代表的事物。

一个好名字通常表达的是“什么”(what),而不是“如何”(how)。

如果一个名字反映了计算机的某些方面而不是问题本身,那么它反映的就是“how”而非“what”了,

请避免选取这样的名字,而应该在名字中反映问题本身!

(一条员工数据:称作:inputRec或employeeData。inputRec是一个反映输入、记录 这些计算机术语的,不能反映问题特征)

 

当变量名的长度在10到16个字符时,调试程序所花的力气是最小的。

 

记住把限定词加到名字最后,变量名最重要的部分,即为变量赋予主要含义的部分应当位于最前面。

特例: Num的限定词的位置是约定俗成的。

Num放在变量的开始位置代表一个总数;例:numCustomers表示员工总数

Num放在变量名的结束位置代表一个序号;例:customerNum表示员工号

避免此问题的方法:

用Conut或Total来代表总数,用Index来代表序号。

例:customerCount员工总数 customerIndex 员工序号

 

命名的一致性可提高可读性,简化维护工作。

如果你发现自己需要猜测某段代码的含义时,就该考虑为变量重新命名。

 

变量名中的对仗词:

next/previous

source/destination 

。。。。。

1、为状态变量命名

标记的名字中不应该含有flag。标记应该用枚举类型、具名常量。

dataReady recalaNeeded 都是好名字

2、为布尔变量命名

以下是几个推荐的布尔变量名(可在其前加上具体的描述名称)

done:用done表示某件事已经完成。(在事情完成之前把done设为false,在完成之后设为true)

error:用error表示有错误发生。(在错误发生之前把变量值设置为false,在错误已经发生时把它设置为true)

found:用found来表示某个值已经找到了。(在还没有找到该值的时候把它设为false,找到之后设为true)

success或ok 用来表明一项操作时候成功。

(不要在布尔变量的前面加上Is)

 

命名规则可以根据局部数据、类数据、全局数据的不同而有所差别。

命名规则可强调相关变量之间的关系。

 

★命名规则的指导原则:

       区分变量名和子程序名:

       变量名和对象名以小写字母开始,子程序名以大写字母开头。

 

       区分类和对象:

       1、通过对对象采用更明确的名字区分类型和变量

       例:Widget employWidget ;

       2、通过给变量加"a"前缀区分类型和变量

       例:Widget aWidget      ;

 

       标识全局变量:

       在全局变量名前加上"g_"前缀

 

       标识成员变量:

       在成员变量名前加上"m_"前缀,可明确表示该变量既不是局部变量,也不是全局变量。

 

       标识自定义类型:

       在自定义类型名前加上"t_"前缀,可明确表示一个名字是类型名,可避免类型名与变量名的冲突

 

       标识枚举类型:

       在枚举类型名前加"e_"前缀,同时为该类型的成员名增加特定类型的前缀。

       例:Color_或Planet_

 

       标识只读参数:

       在其前加上 const前缀,可防止给只读变量赋值的错误。例:constMax

 

变量名要包含以下三类信息:

       1、变量的内容(它代表什么)

       2、数据的种类(具名变量、简单变量、用户自定义类型、类)

       3、变量的作用域(局部的、类的、全局的)

      

关于子程序

好的子程序名:

       给子程序命名的重点是尽可能含义清晰,即:子程序的长短要视该名字是否清晰易懂而定。

       子程序的名字应当描述其所有输出结果以及副作用。

       (例:一个子程序的作用是计算报表总额并打开一个输出文件。若把它命名为computeReportTotals()还不算完整。computeReportTotalsAndOpenOutputFile()很完整但是名字太长。解决方法是 你应该换一种方式编写程序,直截了当地解决问题而不产生副作用。[即:UNIX的哲学,让一个模块只干一件事!])     

 

       给子程序起名时要用动词加宾语的形式。例:PrintDocument()

       在面向对象语言中,不必在过程名中加入对象的名字,因为对象本身就已经包含在调用语句中了。例:Document.Print() ;

【子程序的名字是它质量的指示器,如果名字糟糕且又不准确,那么它就反映不出程序是干什么的。糟糕的名字都意味着程序需要修改】

 

正确地使用输入参数:

       1、对于在函数体中不变更的参数,用const关键字来限制。


       2、如果你假定了传递给子程序的参数具有某种特征,那就要对这种假定进行说明。比注释还好的方法是在代码中使用断言(assertions)

[对参数接口的假定进行说明:

1、参数是仅用于输入的、要被修改的、还是仅用于输出的

2、表示数量的参数的单位(英寸,米等)

3、所能接受的数值范围

4、不该出现的特定数值

5、说明状态代码和错误值的含义]

 

如果你向很多不同的子程序传递数据,就请把这些子程序组成一个类,并把那些经常使用的数据用作类的内部数据。

 

如果你觉得把输入、修改、输出参数区分开很重要,那么就建立一种命名规则来对它们进行区分。

可在这些参数名之前加上i_m_ o_ 前缀。也可以用Input_Modify_ Output_ 来当前缀

 

把对子程序的调用和对状态值的判断清楚地分开。把对子程序的调用和状态值的判断写在一行代码中,增加了该条语句的密度,也相应增加了其复杂度。

应该这样:

ouputStatus = report.FormatOutput(formattedReport ) ;

if( outputStatus = Success ) then ...

 

关于宏:

通常认为,用宏来代替函数调用的做法具有风险,而且不易理解,因此,除非必要,否则应该避免使用这种技术。

用给子程序命名的方法给宏函数命名,以便在需要时可以用子程序来替换宏。

宏对于支持条件编译非常有用,但对于细心的程序员来说,除非万不得已,否则是不会用宏来代替子程序的。(可用内联函数来实现宏函数的效果)

节制使用inline子程序!

 

其它:

建议在真正需要用空语句时这样写:

NULL ;

而不是单用一个分号,这就好比汇编里面的空指令,这样做可以明显的区分真正必须的空语句和不小心多写的分号。

 

在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环的次数。

循环要尽可能的短,要使代码清晰,一目了然。

(如果你写的一个循环的代码超过一屏,那么会让读代码的人抓狂的。解决的办法有两个:

第一:重新设计这个循环。确认是否这些操作都必须放在这个循环里;

第二:将这些代码改写成一个子函数。循环中只调用这个子函数即可)

 

对于全局数据(全局变量、常量定义等)必须要加注释。

注释代码段时应注重“为何做(why)”,而不是“怎么做(how)”

对于函数的入口出口参数及函数的功能给出注释。

 

如果你的全局变量不用来多文件共享,那么就加上static,防止同一个载入模块的两个不同外部对象的命名冲突。

 

 

C语言精华(指针、数组、函数) C语言精华,针对指针、数组、函数调用等深入浅出式的讲解,摘抄自blog... 立即下载

相关推荐

c语言精华总结

内容真的不错哦,希望能够给大家带来帮助

2024年最新「C语言精华篇」春眠不觉晓,函数知多少?_c语言打春眠不觉晓(2),2024年最新C C++开发经验谈

1、告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。2、函数的声明一般出现在函数的使用之前。要满足先声明后使用。3、函数的声明一般要放在头文件中的。函数的定义是指函数的具体实现,交待函数的功能实现。这里我们可以简单的说一下◾ add.h放置函数的声明//函数的声明◾ add.c放置函数的实现//函数Add的实现◾ test.c在main即可;int b = 20;

2401_84976300的博客 1084

DeepSeek+Coze扣子搭建智能体(保姆级教程)

DeepSeek+Coze扣子搭建智能体(保姆级教程)

yizhen91y的博客 1万+

C语言总结精华版

第1章 C程序基础1.简单C程序的组成#include &lt;stdio.h&gt; //把输入输出函数的头文件包含进来 int main( )  //main函数:程序开始执行的入口{  //函数体         printf(“Hello  world!\n”); //在屏幕上输出字符串“Hello  world!”//此处:添加自己定义的命令         return0;      ...

pdsu_congshuang的博客 723

AI:Coze、Dify、n8n:三款低代码工具如何选?

Coze、Dify、n8n:三款低代码工具如何选?

Kant2048的博客 2798

【STM32实物】基于STM32的扫地机器人/小车控制系统设计

扫地机器人系统采用分层结构设计,主要包括底层硬件控制层、中间数据处理层和上层用户交互层。底层硬件控制层负责对各个硬件模块进行控制和数据采集,中间数据处理层负责对采集到的数据进行处理和解算,上层用户交互层负责与用户进行交互并显示系统状态信息。

嵌入式阿齐Archie 1万+

C语言的精髓综述

C语言,作为一种古老而又历久弥新的编程语言,自其诞生以来,便以其独特的魅力和强大的功能在编程界占据了举足轻重的地位。C语言的精髓,不仅体现在其语法结构的简洁明了上,更在于其深邃的设计理念和广泛的应用场景。以下是对C语言精髓的深入综述。

byte轻骑兵的技术小窝 1万+

华为OD机试真题 新系统 2026-07-22 Java&Go&C 实现【最小代价完成论文评审】

某校有 n 篇论文需要分配给教师评审。 每篇论文 i 可由 files[i] 对应的教师列表中任一教师评审。 每篇论文至少需要一名教师评审。 每位教师 t 不论被分配多少篇论文,其评审费用固定为 cost[t]。 请给出参与评审教师数量最少时的总费用。 如果存在多种方案满足教师数量最少,则选择总费用最小的方案。

MISAYAONE的博客 137

【c语言】C语言精华部分

经过一段时间的学习,我们的c语言学习已经告一段落,今日特做以总结。 一、关于c语言的一些基础概念 和大多数程序员一样,我们所学的第一个程序就是,在屏幕上输出一句话"hello  world",不过我们的是"hello bit". 再然后我们学习了c语言的一些基础知识。   注释: c语言注释/**/ c++// 转义字符: 三字母词: 首先是关键字: 在c99标准中,...

如风逝去 1984

【虚幻引擎UE】UE5 可编辑动态样条线轨迹及AI跟随路径移动(含工程源码)

【虚幻引擎UE】可编辑动态样条线轨迹及AI跟随路径移动

灵境引路人 6217

[AI8051U入门第一步]环境安装和按键控制流水灯

本文介绍了AI8051U单片机的开发环境搭建及GPIO应用。主要内容包括:1)通过ISP软件完成Keil开发环境配置;2)AI8051U特性介绍,包括32位8051内核、浮点运算单元及丰富外设;3)GPIO输入输出模式解析,并附流水灯控制代码;4)提供完整的系统初始化、延时函数及LED控制代码实现。文章配套资源包含完整工程代码,适用于智能硬件及物联网开发。

sinat_58149788的博客 662

C语言精华记录——壹(变量的作用域及存储方式)

悟道系列之——C语言 这是我的第一篇博文。对我来说CSDN并不陌生,但一直以来我都是一个默默的潜水者,可是今天我要冒泡了。特意选了十月一日来重新注册账号,我想赋予这个日子以新的意义。在进入正题之前,我想有必要简单的向大家介绍一下自己。 本人80后后,刚毕业于一所普通师范类学校的化学专业。本人形象猥琐,身无长物,自觉难以为祖国的教育事业服务,遂混迹于程序猿们的队伍之中。 本科四年,前半段浑浑噩

菜鸟的自留地-mooyang 3342

【25届数字IC秋招总结】面试经验10——紫光同芯

25届数字ic面试记录——紫光同芯

lalala0635的博客 857

数值计算的C语言实践:浮点精度、内存布局与工程化实现

数值计算不是纯数学推导,而是数学逻辑在计算机硬件约束下的工程实现。理解IEEE 754浮点标准是掌握误差来源的基础,而C语言的内存模型(如数组连续存储、指针算术、malloc行为)直接决定算法稳定性与性能。浮点精度陷阱(如0.1+0.2≠0.3)、相对误差判据设计、缓存友好的矩阵分块、BLAS调用等,均源于对C语言底层行为的准确建模。这些能力支撑着科学计算、嵌入式仿真、金融建模等高可靠性场景。本文聚焦C语言描述下的数值方法落地关键——从二分法收敛判据到矩阵乘法优化,覆盖浮点数行为直觉、内存布局感知和数学表达

weixin_34056162的博客 414

微服务架构 Spring Cloud 生态快速回顾指南

本文介绍了Spring Cloud在微服务架构中的核心组件及其应用。Spring Cloud提供了服务注册与发现、负载均衡、配置管理和容错机制等功能,简化了分布式系统的构建与管理。通过对这些组件的分析,本文阐述了如何利用Spring Cloud提高微服务系统的可用性、灵活性和可扩展性。最终,本文强调了Spring Cloud作为现代云原生应用开发的重要工具,帮助开发者构建高效、可靠的微服务架构。

曾经“等你生日那天”都遥远得像未来,如今却可欢愉的挥手说“下个十年见” 169万+

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

C语言的奇技淫巧(1-50)

01. 宏定义用do{}while(0) 如果定义的宏函数后面有多条语句,使用这样的方式会有问题: #define FUNC() func1(); func2() if(bRunF) FUNC(); 展开宏定义后会变成: if(bRunF) func1(); func2(); 逻辑就不对了。可以用这一的方式解决,非常好用: #define FUNC() do{func1(); func2();}while(0) 02. 数组的初始化 假如给arr的第2~6元素初始化为5,也

嵌入式软件实战派 1万+

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

谁都不告诉的C语言精华(一)(C语言基础教学)

一学就会,学完不会直接喷我

bobo123355566的博客 657

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

【代码大全】 读书笔记

文章目录第七章 高质量的子程序1.创建子程序的理由2.内聚性3.好的子程序名字4.子程序的参数5.使用函数时要注意的问题第八章 防御式编程1.保护程序免遭非法输入数据的破坏2.断言 assertions3.错误代码的处理4.异常的处理 exception5.各种错误的处理机制:第十章 使用代码的一般事项变量初始化原则作用域 : 使变量引用局部化第十一章 变量名的力量1.注意事项2.变量的...

wxq_1993的博客 848

C语言精华

C语言测试是招聘嵌入式系统程序员过程中必须而且有效的方法。这些年,我既参加也组织了许多这种测试,在这过程中我意识到这些测试能为带面试者和被面试者提供许多有用信息。        从被面试者的角度来讲,你能了解许多关于出题者或监考者的情况。这个测试只是出题者为显示其对ANSI标准细节的知识而不是技术技巧而设计吗?如要你答出某个字符的ASCII值。这些问题着重考察你的系统调用和内存分配策略方面的

wukeokok的专栏 477

C语言精华总结(一)

转自http://zhenguohui.spaces.live.com/blog/cns!9551940217428bc!116.trak 一.指针 它的本质是地址的类型。在许多语言中根本就没有这个概念。但是它却正是C灵活,高效,在面向过程的时代所向披靡的原因所在。因为C的内存模型基本上对应了现在vonNeumann(冯·诺伊曼)计算机的机器模型,很好的达到了对机器的映射。不过有些人似乎永远也不...

hopegrace的博客 545

c语言 精华,C语言课件完整版(精华)

《C语言课件完整版(精华)》由会员分享,可在线阅读,更多相关《C语言课件完整版(精华)(484页珍藏版)》请在人人文库网上搜索。1、1 .计算机计程仪编程基础(习语言),2,1 .教学要求严格。 2 .仔细听,及时做笔记。 3 .上课时使不得八卦。 4 .放学后认真完成课外作业、实验报告。 5 .如有问题,立即与老师联系。 教材和工具书的选择,主教科书区治华,陈天煌等习语言计程仪编程机械工业出版社...

weixin_39623805的博客 552

C语言精华代码

1. /*接受一位整数。编写一个程序,将该数的每一个数字相加并显示结果*/ #include void main() {    int a,b,c,sum;    printf("\n请输入一个数: ");    scanf("%d",&a);    c=0;    sum=0;    while (a>0)    {     b=a%10;     a=a/10;  

Swxctx 2258

C语言图形编程--俄罗斯方块制作(一)详解

效果图   用C语言实现俄罗斯方块,需要先解决下面几个问题: 1、如何用C语言绘制图形界面 EasyX图形库(http://www.easyx.cn)即TC的图形库在VC下的移植。   包含库#include 先初始化图形窗口 initgraph(WINDOW_WIDTH, WINDOW_HIGH) ;WINDOW_WIDTH为窗口的宽带,WINDOW_HIGH为窗口的高度。

菜鸟的自留地-mooyang 16万+

C语言图形编程--俄罗斯方块制作(二)源代码

所有源代码文件,此为本人2年前所作,设计上还有些缺陷。希望大家不吝指正。 /************************ (C) COPYRIGHT 2013 yang_yulei ************************ * File Name : head.h * Author : yang_yulei * Date First Issu

菜鸟的自留地-mooyang 4万+
上一篇: C语言精华记录——玖(常用库函数)
下一篇: 关于C/C++中全局变量的初始化问题的深入思考
Moo-Yan
博客等级 码龄14年 973粉丝 68原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值