C语言之预处理那点事

news2024/10/7 13:14:04

文章目录

  • 一、程序的翻译和执行环境
  • 二、预定义符号的介绍
    • 1.预定义符号
    • 2.#define
    • 3.宏和函数的比较
    • 4.条件编译
  • 总结

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

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

2.构建我们的main函数
在ANSI 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中

在这里插入图片描述我们发现里面的MAX变为我们所定义的10,且没有我们的注释,代码量也从8行变为800多行,这是对include头文件的包含造成的。
编译阶段:

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

这是在Linux下进行汇编的结果。
在这里插入图片描述
我们也可以直接在VS上查看汇编代码:
在这里插入图片描述
汇编:

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

在这里插入图片描述
进行汇编是将汇编语言转化为.o(Windows下为.obj)目标二进制文件,且文件不可执行。
最后进行:

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

在这里插入图片描述
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;
}

在这里插入图片描述

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;
}

让我们看看进行预编译后的结果吧:
在这里插入图片描述
我们可看出对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;
}

在这里插入图片描述
我们看到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和判断是否被定义类似。

总结

预处理可以改变程序设计环境,提高编程效率。所以学好预处理可以对我们起到锦上添花的作用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/632656.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FFmpeg音视频处理工具介绍及应用

1 FFmpeg介绍 FFmpeg项目由 Fabrice Bellard在2000年创立。到目前为止&#xff0c;FFmpeg项目的开发者仍然与VLC、MPV、dav1d、x264等多媒体开源项目有着广泛的重叠。Ffmpeg&#xff08;FastForward Mpeg&#xff09;是一款遵循GPL的开源软件&#xff0c;在音视频处理方面表现…

算法刷题-数组-移除元素

27. 移除元素 力扣题目链接 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并原地修改输入数组。 元素的顺序可以改变。你不需…

chatgpt赋能python:Python如何编写优化SEO的软件

Python如何编写优化SEO的软件 作为一种功能强大且易于学习的编程语言&#xff0c;Python已经成为广泛使用的开发工具之一&#xff0c;其用户群体涵盖从初学者到专业开发人员。然而&#xff0c;在Python编写SEO相关软件时&#xff0c;开发人员需要遵循一些最佳实践&#xff0c;…

chatgpt赋能python:Python中如何加空格

Python中如何加空格 Python是一门广泛应用于科学计算、数据分析、人工智能、Web开发等领域的高级编程语言。在Python编程过程中&#xff0c;经常需要使用到空格&#xff0c;以实现程序的格式化和美观&#xff0c;同时也有助于提高代码的可读性和可维护性。本文主要介绍Python中…

人工蜂群算法(Artificial Bee Colony (ABC) Algorithm,附简单案例及详细matlab源码)

作者&#xff1a;非妃是公主 专栏&#xff1a;《智能优化算法》 博客地址&#xff1a;https://blog.csdn.net/myf_666 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录 专栏推荐一、人工蜂群算法二、伪代码三…

前端vue地图定位并测算当前定位离目标位置距离

前端vue地图定位并测算当前定位离目标位置距离, 下载完整代码请访问uni-app插件市场地址: https://ext.dcloud.net.cn/plugin?id12974 效果图如下: # #### 使用方法 使用方法 <!-- // 腾讯地图key注册地址&#xff08;针对H5端&#xff0c;manifest.json中web配置&…

【力扣刷题 | 第六天】

目录 前言&#xff1a; 344. 反转字符串 - 力扣&#xff08;LeetCode&#xff09; 541. 反转字符串 II - 力扣&#xff08;LeetCode&#xff09; 今天我们进入字符串章节的刷题旅程&#xff0c;希望各位小伙伴可以和我一起坚持下去&#xff0c;一起征服力扣&#xff01; 前言…

chatgpt赋能python:Python如何删除列表中的重复数据

Python如何删除列表中的重复数据 介绍 Python是一种高级编程语言&#xff0c;可用于开发各种类型的应用程序&#xff0c;包括网站&#xff0c;桌面应用程序&#xff0c;数据分析和机器学习。在Python编程中&#xff0c;经常需要对列表中的数据进行操作。有时候&#xff0c;我…

【读书笔记】《蛤蟆先生去看心理医生》- [英] 罗伯特·戴博德

文章目录 第一章 整个人都不太好第二章 挚友前来相助第三章 初见咨询师第四章 抑郁的原因第五章 成长的寓言第六章 探索童年第七章 愤怒的表现第八章 意外访客第九章 秘密协议第十章 午餐聚会第十一章 蛤蟆先生的选择第十二章 说出人生故事第十三章 人生坐标与心理游戏第十四章…

动态规划II (42、53、64、70、72)

CP42 接雨水 题目描述&#xff1a; 学习记录&#xff1a; 虽然脑子里第一个蹦出双指针&#xff0c;但是题目是动态规划&#xff0c;两个混着想&#xff0c;啥也没想出来...不会 1.动态规划&#xff1a;太牛了吧&#xff0c;这个不是从整体去考虑每一块该怎么填&#xff0c;而…

Baumer工业相机堡盟工业相机如何使用BGAPISDK的相机图像时间戳计算运行时间以及时间差(C++)

Baumer工业相机堡盟工业相机如何使用BGAPISDK的相机图像时间戳计算运行时间以及时间差&#xff08;C&#xff09; Baumer工业相机Baumer工业相机BGAPI SDK和图像时间戳的技术背景Baumer工业相机使用BGAPISDK控制相机数据流的方式1.引用合适的类文件2.使用BGAPISDK获取时间戳的方…

动态规划I (45、55、62、63)

按顺序刷确实效率太低了&#xff0c;今天开始要按顺序的同时也按标题来了&#xff0c;全面加油&#xff01;这种应该以后会更多直接总结题解了&#xff0c;自我学习用&#xff0c;全靠大佬&#xff0c;贴贴&#xff01;&#xff01;含45、55、62、63 CP55 跳跃游戏 题目描述&…

【浅谈DBA职业生涯---误操作篇】

&#x1f448;【上一篇】 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 【下一篇】&#x1f449; &#x1f53b;【&#x1f4a3; 话题引入&#xff1a;请列举你在从事 DBA 生涯中,最难以忘怀的一次误操作】 &#x1f6a9; 该话题覆盖…

MapReduce共享单车练习

MapReduce 本机运行 文章目录 MapReduce 本机运行✅前置工作1. 配置JDK2. 创建Java项目3. 导入所需JAR包 编程实现以下题目1. 统计各个月份共享单车使用的总数2. 统计不同天气情况下共享单车使用的总数3. 统计每个季度共享单车使用的总数4. 统计每个月份的注册数量5. 统计每天1…

【Docker】docker部署前Springboot-vue后端分离项目实战

文章目录 docker 安装jdkdocker 安装mysql通过Docker命令进入Mysql容器内部初始化数据sqlDbx连接查看构建后端镜像查看构建的后端镜像运行后端镜像 docker 安装nginx docker 安装jdk https://jackwei.blog.csdn.net/article/details/110227719 docker search openjdk:8 docke…

python语法-数据可视化(全球GDP动态柱状图开发)

python数据可视化&#xff08;全球GDP动态柱状图开发&#xff09; 开发工具&#xff1a;pycharm、pyecharts模块 &#xff08;项目数据见文末参考内容&#xff09; """ 演示GDP动态柱状图开发 """from pyecharts.charts import Bar,Timeline fr…

Linux系统运行时参数命令(性能监控、测试)(1)监控工具、CPU上下文切换、CPU性能监控

目录 1. 监控工具2. CPU性能监控2.1 平均负载和CPU使用率2.1.1 平均负载基础2.1.2 使用uptime命令分析平均负载2.1.3 平均负荷和CPU使用率 2.2 CPU上下文切换2.2.1 什么是CPU上下文切换2.2.2 有哪些上下文切换2.2.3 怎么查看上下文切换 vmstat2.3 遇到CPU使用率高该如何排查 主…

openGauss5.0之学习环境 Docker安装

文章目录 0.前言1. 准备软硬件安装环境1.1 软硬件环境要求1.2 修改操作系统配置1.2.1 关闭操作系统防火墙 1.3 设置字符集参数1.4 设置时区和时间&#xff08;可选&#xff09;关闭swap交换内存1.5 关闭RemoveIPC1.6 关闭HISTORY记录 2. 容器安装2. 1支持的架构和操作系统版本2…

大语言模型之人类反馈学习RLHF

在2017年左右&#xff0c;深度强化学习&#xff08;Deep Reinforcement Learning&#xff09;逐渐兴起并引起广泛关注。特别是在2017年6月&#xff0c;OpenAI与Google DeepMind联合推出了一项名为《Deep Reinforcement Learning from Human Preferences》&#xff08;RLHF&…

chatgpt赋能python:Python如何判断整数

Python如何判断整数 Python是一个简单易学的编程语言&#xff0c;但是对于初学者来说&#xff0c;判断整数可能会有一些困难。在本文中&#xff0c;将介绍Python如何判断整数&#xff0c;并提供一些示例帮助您更好地理解。 如何判断整数 在Python中&#xff0c;判断整数可以…