C语言:预编译过程的剖析

news2024/11/29 20:41:54

目录

一.预定义符号和#define定义常量

二.#define定义宏

三.宏和函数的对比

四、#和##运算符

五、条件编译


在之前,我们已经介绍了.c文件在运行的过程图解,大的方面要经过两个方面。

一、翻译环境

1.预处理(预编译)

2.编译

3.汇编

4.链接

二、运行环境

我们在这里,主要介绍以下预处理阶段的事情,重点是#define定义宏宏和函数对比的各自优点和缺点

 

预处理阶段主要处理那些源文件中#开始的预编译指令。比如:#include,#define,处理的规则如下:

(1)将所有的 #define 删除,并展开所有的宏定义

(2)处理所有的条件编译指令,如:#if、#ifdef、#elif、#else、#endif

(3)处理#include 预编译指令将包含的头文件的内容插入到该预编译指令的位置。这个过程是递归进行的,也就是说也被包含的头文件可能包含其他文件

(4)删除所有的注释

(5)添加行号和文件名标识,方便后续编译器生成调试信息等。

(6)保留所有的#pragma 的编译器指令,编译器后续会使用。

一.预定义符号和#define定义常量

1)预定义符号

在C语言中,设置了一些预定义符号,可以直接使用,预定义符号也是在预处理期间处理的

1.    _ _FILE_ _          //进行编译的源文件

2.    _ _LINE_ _          //文件当前的行号

3.    _ _DATE_ _         //文件被编译的日期

4.    _ _TIME_ _          //文件被编译的时间

5.    _ _STDC_ _          //如果编译器遵循ANSI  C,其值为1,否则未定义

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
	FILE* pf = fopen("test1.txt", "w+");
	printf("file:%s   line:%d   date:%d\n", __FILE__, __LINE__, __DATE__);
	return 0;
}

这里,可以看出,我们是可以直接运行这些与定义符号的。 

 2)#define定义常量

# define MAX 100
# define reg register     //为register这个关键字,创建一个新的名字
#define do_forever for(;;)    //死循环,起一个更加形象的名字
#define CASE break;case      //在写case语句的时候自动把break写上
//如果定义的stuff过长,可也分成几行写, 除了最后一行外,每行的后面都加上一个反斜杠(续行符)
#define DEBUG_PRINT printf("fine:%s  tline:%d\t  \
                          date:%s \ time:%s\n    \
                           ",__FILE__,__LINE__,  \
                             __DATE__,TIME__   )

这里需要注意的是,当定义的语句过长的时候,用  '\'来换行继续写,这里当续行符来使用

注意:在使用#define定义标识符的时候不要在最后加上分号(防止我们出错) 

二.#define定义宏

#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏或者定义宏(define macro)。

#define name(  parament-list  )    stuff

这里 parament-list  是一个由逗号隔开的符号表,它们可能出现在stuff中。

注意:参数列表的左括号必须与name紧邻,如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分

举例:

#define SQUARE( x )  x*x
int a = 5;
printf("%d\n", SQUARE( a + 1));

这里看这一段代码,我们可能会说执行的结果为36,但是实际上它将打印11

我们为了得到36的正确结果,在定义宏的时候,为了避免出现的错误,我们一般是在宏定义表达式的两边加上一对括号

#define DOUBLE(x)   (  (x) + (x)  ) 

 注意:所有用于对数值表达式进行求值得宏定义都应该用这种加括号的方式避免在使用宏时由于参数中得操作符或操作符之间不可预料得相互作用。

宏的替换规则

1.在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换

2.替换文本随后被插入到程序中原来文本的位置。对于宏, 参数名被它们的值所替换

3.最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就是重复上述处理过程。

 注意:1 宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归

            2 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

三.宏和函数的对比

1.宏通常被应用于简单的运算。(如下:)

#define MAX(a,b)    (  (a) > (b) ? (a) : (b)  )

 函数来完成上述代码比较两数大小也是可以的,但是于宏相比,宏更好

宏的优势:

1.宏比函数再程序的规模和速度上更胜一筹。

2.宏的参数是于类型无关的。(很重要)

3. 宏的参数可以出现类型,函数做不到。(很重要)

宏的参数出现了类型。 

#define MALLOC(num, type)  (type )malloc(num*sizeof(type))
MALLOC(10,int);


//上述的代码,相当于下面的代码
(int*)malloc(10 * sizeof(int));

 宏的劣势:

1.每次使用宏的时候,一份宏定义的代码将插入到程序中除非宏比较短否则可能大幅度增加程序的长度

2.宏是没办法调试的。

3.宏由类型无关,也就不够严谨。

4.宏可能会带来运算符优先的问题,导致程序容易出错。

宏和函数的对比
属性#define定义宏函数
代码长度每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外,程序长度会大幅度增加函数的代码只出现于一个地方;每次使用函数的时候,都调用那个地方的同一份代码
执行速度更快存在函数的调用和返回的额外开销,相对慢一些
操作符优先级宏参数的求值是再所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多加括号函数参数只在函数调用的时候求值一次,将结果值传递给函数。
带有副作用的参数参数可能被替换到宏中的多个位置,如果宏的参数被多次计算,带由副作用的参数求值可能会产生不可预料的结果。函数参数只在传参的时候求值一次,结果容易控制
参数类型宏的参数与类型无关,只要对参数的操作是合法的,它就可以使用于任何参数类型。函数的参数是与类型有关的,如果参数类型不同,就需要不同的函数,即使它们的任务是不同的。
调试宏不方便调试函数可以逐语句调试
递归不能递归可以递归

四、#和##运算符

1.  #运算符将宏的一个参数转换为字符串字面量。它仅允许出现在带参数的宏的替换列表中。

2.  #运算符所执行的操作可以理解为“字符串化”

int a = 10;
#define PRNT(n) printf("the value of "#n " is %d",n);

##运算符:可以把位于它两边符号和成一个符号,它允许宏定义从分离的文本片段创建标识符##被称为记号粘合 

当定义一个比较两个数较大值的时候,类型不同的数据就得写不同的函数,例如:

int int_max(int x, int y)
{
	return x > y ? x : y;
}

float float_max(float x, float y)
{
	return x > y ? x : y;
}

这时候,用##来实现宏定义,这时候就非常简单了,下面的代码只要传不同的类型,就可以实现不同类型的函数定义。

#define GENERIC_MAX(type)         \
type type##_max(type x ,type y)   \
{                                 \
	return (x > y ? x : y);       \
}                                 \

五、条件编译

在执行编译一个程序的时候,我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。

1.

#if  常量表达式

#endif

2.多个分支的条件编译

#if      常量表达式

#elif     常量表达式

#else

#endif

3.判断是否被定义

#if defined(symbol)

#ifdef

#if !defined(symbol)

#ifdef symbol

4.嵌套指令(举例)

#if defined(OS_UNIX)

               #ifdef OPTION1

                          unix_version_option1();

                #endif

                #ifdef OPTION2

                           unix_version_option2();

                #endif

#elif defined(OS_MSDOS)

                #ifdef OPTION2

                            msdos_version_option2();

                #endif

#endif

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

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

相关文章

SpringBoot在线教育系统:从零到一的构建过程

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理微服务在线教育系统的相关信息成为必然。开…

如何禁止电脑蓝牙?分享五个有效方法,快@企业老板来看! 内行人都在用的方法哦,不妨一试!

在现代办公环境中&#xff0c;电脑蓝牙功能的便捷性无可否认&#xff0c;但随之而来的安全风险也不容忽视。 尤其对于企业而言&#xff0c;蓝牙的开放可能引发数据泄露、未经授权的设备连接等一系列安全问题。 因此&#xff0c;如何有效地禁止电脑蓝牙&#xff0c;成为了许多…

终于有人把多模态大模型讲这么详细了

多模态大型语言模型&#xff08;Multimodal Large Language Models&#xff0c; MLLM&#xff09;的出现是建立在大型语言模型&#xff08;Large Language Models&#xff0c; LLM&#xff09;和大型视觉模型&#xff08;Large Vision Models&#xff0c; LVM&#xff09;领域不…

付费计量系统通用功能(14)

11.18 Function_Object功能目标 11.18.1 Function_Object definition功能目标定义 A Function_Object is an abstract notation of defining the attributes of a function. 功能目标是一个抽象的功能属性的定义 Definition of a function object should stat…

Linux plt表调用汇编代码分析

linux调用共享库中的函数时通过plt表和got表实现位置无关代码&#xff0c;过程中涉及到lazy binding&#xff0c;即在第一调用外部函数时解析被调用的函数地址并将地址写入到got表&#xff0c;后续调用则不需要解析函数地址&#xff0c;具体过程如下 1.c程序如下 #include &l…

基于Node2Vec的图嵌入实现过程

目录 一、引言二、Node2Vec&#xff08;原理&#xff09;2.1 随机游走&#xff08;Random Walk&#xff09;2.2 嵌入学习2.3 Node2Vec 的优势 三、使用 Node2Vec 进行图嵌入&#xff08;实践&#xff09;3.1 读取和转换 JSON 文件为 Graph 对象3.2 训练 Node2Vec 模型3.3 二维嵌…

Python+Django微信小程序前后端人脸识别登录注册

程序示例精选 PythonDjango微信小程序前后端人脸识别登录注册 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《PythonDjango微信小程序前后端人脸识别登录注册》编写代码&#xff0c;代码整…

2024年10月6日历史上的今天大事件早读

23年10月06日西汉“新莽政权”领袖王莽被刺身亡 1866年10月06日清政府批准筹设天津机器局 1905年10月06日俄国爆发铁路工人大罢工 1913年10月06日中、英西姆拉会商“西藏问题” 1927年10月06日阿尔-乔尔森主演第一部有声电影 1940年10月06日新四军获黄桥决战胜利 1949年1…

字节跳动员工玩转 AI 的 100 种办法

今年&#xff0c;字节跳动在国内上线了 AI 应用开发平台——扣子。用户可以根据自己的想法在扣子上创建 chat bot&#xff08;聊天机器人&#xff09;&#xff0c;甚至能利用扣子 API 上线一个产品。 扣子上有丰富的插件工具&#xff0c;如果用户希望 bot 具备某个功能&#x…

sql-labs靶场第九关测试报告

目录 一、测试环境 1、系统环境 2、使用工具/软件 二、测试目的 三、操作过程 1、寻找注入点 2、注入数据库 ①寻找注入方法 ②爆库&#xff0c;查看数据库名称 ③爆表&#xff0c;查看security库的所有表 ④爆列&#xff0c;查看users表的所有列 ⑤成功获取用户名…

2024年,现在做全职的AI产品经理,时机对不对?

就在最近&#xff0c;Chatgpt又更新了&#xff0c;推出了新版本名字叫做canvas。 这个版本可以支持对AI的对话内容进行编辑了&#xff0c;也就是以前prompt一次性生成的结果总是很难控制输出结果&#xff0c;新版新增了可以在结果内容与用户进行交互的产品入口&#xff0c;最终…

(20)MATLAB使用卡方(chi-square)分布生成Nakagami-m分布

文章目录 前言一、使用卡方分布函数生成Nakagami分布随机变量二、MATLAB仿真1.仿真代码2.运行结果 前言 Nakagami分布随机变量的生成&#xff0c;可以使用gamma分布实现&#xff0c;也可以使用卡方&#xff08;chi-square&#xff09;分布随机变量生成。使用伽马&#xff08;g…

AI 写作工具汇总

&#x1f423;个人主页 可惜已不在 &#x1f424;这篇在这个专栏AI_可惜已不在的博客-CSDN博客 &#x1f425;有用的话就留下一个三连吧&#x1f63c; 目录 前言: 正文: ​ 前言: 在信息时代的浪潮中&#xff0c;AI 写作应运而生。它以强大的算法和海量的数据为支撑&…

文本处理 — JavaScript 中的字符串——WEB开发系列44

掌握它所出现的文本是很有用的&#xff0c;字符串是最基本的数据类型之一&#xff0c;承载着我们与计算机之间的交流。它们可以用来表示文本、传递信息、构建用户界面等。JavaScript作为一种广泛使用的编程语言&#xff0c;为字符串提供了强大而灵活的处理功能。 一、创建字符串…

图解 Stable Diffusion 如何实现文生图

前言 1引言 Stable Diffusion 是 2022 年发布的深度学习文生图模型。它主要用于根据文本的描述产生详细图像&#xff0c;尽管它也可以应用于其他任务&#xff0c;如内补和外补绘制&#xff0c;以及在提示词指导下产生图生图的翻译。 算法原理 Stable Diffusion 是一种扩散模…

Python+Django预约管理系统

程序示例精选 PythonDjango预约管理系统 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《PythonDjango预约管理系统》编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。 学习…

【深度学习基础模型】液态状态机(Liquid State Machines, LSM)详细理解并附实现代码。

【深度学习基础模型】液态状态机&#xff08;Liquid State Machines, LSM&#xff09;详细理解并附实现代码。 【深度学习基础模型】液态状态机&#xff08;Liquid State Machines, LSM&#xff09;详细理解并附实现代码。 文章目录 【深度学习基础模型】液态状态机&#xff0…

No.4 笔记 | 探索网络安全:揭开Web世界的隐秘防线

在这个数字时代&#xff0c;网络安全无处不在。了解Web安全的基本知识&#xff0c;不仅能保护我们自己&#xff0c;也能帮助我们在技术上更进一步。让我们一起深入探索Web安全的世界&#xff0c;掌握那些必备的安全知识&#xff01; 1. 客户端与WEB应用安全 前端漏洞&#xff1…

【PACS源码】C#.net医学影像管理系统源码,支持CT、MR、CR、DR、ECT、DSA、X光机、超声、内镜、病理等多种设备。

PACS医学影像管理与传输系统软件可对医学仪器输出的视频信号进行接收、处理、存储、报告输出、管理、查询等&#xff0c;并支持网络&#xff0c;实现资源共享。为医院对病人信息资料进行数字化、科学化、网络化管理提供了必要的工具。 基于DICOM标准的PACS医学影像管理系统&am…

【C++】二叉搜索树+变身 = AVL树

&#x1f680;个人主页&#xff1a;小羊 &#x1f680;所属专栏&#xff1a;C 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 前言一、AVL树二、AVL树的实现2.1 平衡因子2.2 旋转处理2.2.1 左单旋&#xff1a;插入新节点后单纯的右边高2.2.2 …