C语言之预处理那点事

这篇具有很好参考价值的文章主要介绍了C语言之预处理那点事。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


在C语言中,曾出现各种各样新的标准,有的昙花一现,有的则源远流传。我们这篇来看流传下来的,简化开发者编程和提升性能的一种精粹“预处理”。

一、程序的翻译和执行环境

2.构建我们的main函数
在ANSI C的任何一种实现中,存在两个不同的环境。
第一种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。
第二种为运行环境,他用于实际执行代码。
C语言之预处理那点事
翻译环境有有一下几步操作:
C语言之预处理那点事
经过汇编过程生成目标文件,在经过链接完成翻译环境的工作,生成一个可执行程序文件。
当有多个源文件时:
C语言之预处理那点事链接器的目的是把多个源文件生成的目标文件进行整合,从而形成一个单一而完整的可执行程序。
链接器也会引入标准C函数库中任何被改程序所用到的函数,而且他还可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。
C语言之预处理那点事
我们用Linux系统来具体的演示一下每个阶段:
下面一串代码为我们测试使用的代码:

#include<stdio.h>
#define MAX 10
int main()
{
	int a = MAX;//给a赋值
	printf("%d", a);
	return 0;
}

预编译阶段:

gcc -E test.c > test.i//将test.c进行预编译,并把编译的内容重定向到test.i中

C语言之预处理那点事我们发现里面的MAX变为我们所定义的10,且没有我们的注释,代码量也从8行变为800多行,这是对include头文件的包含造成的。
编译阶段:

gcc -S test.i > test.s//将test.i进行编译,并把编译的内容重定向到test.s中

这是在Linux下进行汇编的结果。
C语言之预处理那点事
我们也可以直接在VS上查看汇编代码:
C语言之预处理那点事
汇编:

gcc -c test.s > test.o//将test.s进行汇编,并把编译的内容重定向到test.o中

C语言之预处理那点事
进行汇编是将汇编语言转化为.o(Windows下为.obj)目标二进制文件,且文件不可执行。
最后进行:

gcc test.o -o test//生成test的可执行程序,-o代表对生成的可执行程序进行命名

C语言之预处理那点事
ls显示当前目录的文件,./test为执行该程序。
运行环境:
程序的执行过程:
1.程序必须载入内存中。
2.程序执行开始,进行main函数调用。
3.开始执行程序代码。这个时间程序将使用一个运行时堆栈(stack),用来存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,用来存储他们的值
4.终止程序。正常终止main函数,或者程序异常终止。

二、预定义符号的介绍

1.预定义符号

__FILE__ //进行编译的源文件
__LINE__ //文件当前行号
__DATE__ //文件被编译日期
__TIME__ //文件被编译时间

这些预定义符号都是语言内置的。
例如:

int main()
{
	printf("file:%s\nline:%d\ndata:%s\ntime:%s", __FILE__, __LINE__, __DATE__, __TIME__);
	return 0;
}

C语言之预处理那点事

2.#define

语法:

#define name stuff

例如:

#define MAX 10
#define un unsigned //为unsigned创建一个更简短的名字

替换规则:
在程序中拓展#define定义符号和宏时,需要一下几个步骤:
1.调用宏时,先对参数进行检查,看是否包含任何由#define定义的符号,如果有,它们首先被替换。
2.替换的文本随后被插入到程序中原来的位置
3.最后再次对文本进行扫描,重复1,2过程。
注意:
1.宏参数和#define定义中可以出现其他#define定义的变量。对于宏来说,不能出现递归。
2.当预处理器搜索#define定义的符号时,字符串常量的内容并不被搜索。
下面我们看几个宏的错误示例:

#define MAX = 10 //多了一个等号
#define MIN 1;//多了一个分号
#define ADD(x,y) x*y//x*y没有加括号,正确应为(x)*(y)
int main()
{
	int a = MAX;
	int b = MIN;
	ADD(5 + 1, 6 + 1);
	return 0;
}

让我们看看进行预编译后的结果吧:
C语言之预处理那点事
我们可看出对a进行赋值时多了一个等号,对b赋值时末尾多一个分号,进行宏替换的x和y的运算顺序也和我们所想的不一样。
宏是替换,使用宏要注意宏所编写的是否正确。
带副作用的宏参数:
当宏参数在宏的定义中出现超过一次的时间,如果参数带有副作用,那么使用这个宏可能会出现危险,导致不可预测的后果。副作用就是表达式求值的时间出现的永久性效果。例如:

x+1;//不带副作用
x++;//带副作用

下面我们用一个具体例子来看下结果:

#define MAX(X,Y) ((X)>(Y)?(X):(Y))
int main()
{
	int a = 10;
	int b = 11;
	int max = MAX(a++, b++);
	printf("%d\n", max);
	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}

C语言之预处理那点事
我们看到a和b的值发生了变化。这就是副作用宏。
#undef:用来移除一个宏定义。

3.宏和函数的比较

上面宏定义的运算我们为什么不用函数来执行呢?原因有一下两个方面:
1.用于调用函数和函数返回的代码的时间可能比执行这个代码所需更多时间。所以宏比函数在程序的规模和速度上更胜一筹。
2.函数的参数必须要指明特定的类型。而宏可以适用于任何类型。宏是和类型无关的
宏和函数相比劣势也很明显:
1.每次使用宏时,需要进行宏替换,如果宏比较长,则可能大幅增加程序的长度。
2.宏是无法调试的
3.宏是和类型无关的,也就不够严谨
4.宏可能带来运算符优先级的问题,导致程序出错。
命名约定:
一般来说函数和宏使用语法很相似。所以语言本身无法帮我们区分。所以出现了命名约定。宏名全部为大写,函数名不要全部大写

4.条件编译

在编译一个程序的时间如果我们要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。比如:用来调试的代码,头文件是否包含等。
1:

#if 常量表达式
	//。。。。
#endif
//常量表达式由预处理器求值
#define __DEBUG__ 1
int main()
{
	int a = 0;
	#if __DEBUG__
	a = 10;
	#endif
	printf("%d", a);
	return 0;
}

2:多个分支的条件编译条件

#if 常量表达式
	//。。。。
#elif 常量表达式
//。。。。
#endif

用法和上面相同。
3.判断是否被定义

#if defined(symbol)
#ifdef symbol

#if !defined(symbol)
#ifdef symbol

判断是否被定义通常用于判断是否重复包含头文件,#pragma once和判断是否被定义类似。

总结

预处理可以改变程序设计环境,提高编程效率。所以学好预处理可以对我们起到锦上添花的作用。文章来源地址https://www.toymoban.com/news/detail-478643.html

到了这里,关于C语言之预处理那点事的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C语言【预处理器】

    1、一些关于预处理的知识 ​C代码中,一般带 # 的都是预处理指令,包括 宏替换、文件包含、条件编译 等。 ​为兼容一些老编译器, # 前后一般不写空格 ​预处理指令后面不加分号。 2、宏定义 3、文件包含 ​自定义头文件,用\\\" \\\" 。 引号里填相对路径或绝对路径。基于当

    2024年02月05日
    浏览(34)
  • C语言·预处理详解

            C语言设置了一些预定义符号,可以直接使用,预定义符号也是在预处理期间处理的                 __FILE__  进行编译的源文件                 __LINE__  文件当前的行号                 __DATE__  文件被编译的日期                 _

    2024年01月21日
    浏览(36)
  • C语言预处理详解

    上一篇博客中我们讲了C语言的编译与链接,在编译过程中有三个小阶段:预处理、编译、汇编。 本篇博客将详细讲述预处理部分的有关知识点 。 在C语言中,C语言本身设置了⼀些预定义符号,可以直接使⽤ ,预定义符号的处理也是在预处理期间进行的。 在这里介绍几个常

    2024年04月15日
    浏览(41)
  • C语言:预处理详解

    创作不易,来个三连呗! C语⾔设置了⼀些预定义符号, 可以直接使⽤ ,预定义符号也是在预处理期间处理的。 __FILE__ //进⾏编译的源⽂件 __LINE__ //⽂件当前的⾏号 __DATE__ //⽂件被编译的⽇期 __TIME__ //⽂件被编译的时间 __STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义

    2024年01月19日
    浏览(32)
  • 【C语言】预处理详解

             本文目录 1 预定义符号 2 #define 2.1 #define 定义标识符 2.2 #define 定义宏 2.3 #define 替换规则 2.4 #和## 2.5 带副作用的宏参数 2.6 宏和函数对比 2.7 命名约定 3 #undef 4 命令行定义 5 条件编译 6 文件包含 6.1 头文件被包含的方式 6.2 嵌套文件包含 这些预定义符号都是语言内置

    2024年02月14日
    浏览(32)
  • 详解C语言—预处理

    目录 一、预处理 1、预定义符号介绍 2、预处理指令 #define #define 定义标识符:  #define 定义宏: #define 替换规则 3、预处理操作符# 4、预处理操作符## 5、带副作用的宏参数 6、宏和函数对比 二、命名约定 三、预处理指令 #undef 四、命令行定义 五、条件编译  1、单分支#if:

    2024年02月08日
    浏览(43)
  • 自然语言之文本预处理

    概念 分词就是将连续的字序列按照一定的规范重新组合成词序列的过程。在英文的行文中,单词之间是以空格作为自然分界符的,而中文只是字、句和段能通过明显的分界符来简单划界,唯独词没有一个形式上的分界符。分词过程就是找到这样分界符的过程. 作用 词作为语言

    2024年02月06日
    浏览(29)
  • <C语言> 预处理和宏

    这些预定义符号都是C语言内置的。 举个例子: #define 定义标识符形式: 其中, 标识符 是你希望定义的名称,而 值 可以是一个数值、一个字符串或一个表达式。 例子: #define 只是进行简单的文本替换,没有类型检查和错误检查。 建议 #define 后面不要加分号 #define机制包括

    2024年02月14日
    浏览(25)
  • 【C语言:编译、预处理详解】

    我们都知道,一个程序如果想运行起来要经过编译、链接然后才能生成.exe的文件。 编译⼜可以分解为三个过程: 预处理(有些书也叫预编译)、 编译 汇编 预处理阶段 主要处理那些源文件中以#开始的预编译指令。比如:#include,#define,处理的规则如下: 删除所有的注释

    2024年02月03日
    浏览(37)
  • 【C语言进阶】预处理详解

    对预处理的相关知识进行详细的介绍                  ✨  猪巴戒 :个人主页✨                 所属专栏 :《C语言进阶》         🎈 跟着猪巴戒 ,一起学习C语言🎈 目录 引言 预定义符号 #define定义常量 #define定义宏 带有副作用的宏参数 宏替换的规则 宏函数的

    2024年01月23日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包