C/C++程序设计和预处理
个人主页:仍有未知等待探索_C语言疑难,数据结构,小项目-CSDN博客
专题分栏:C语言疑难_仍有未知等待探索的博客-CSDN博客
目录
一、引言
二、程序的翻译环境和执行环境
1、什么是程序
2、程序的翻译环境
3、程序的执行环境
三、预处理
1、预定义符号
2、#define
1.#define定义标识符
2.#define定义宏
3.#define的替换规则
注意
思考题
4.#和##
1.#的作用
2.##的作用
5.带副作用的宏参数
6.宏和函数对比
宏的优势
函数的优势
7.命名约定
3、#undef
功能
4、命令行定义
5、条件编译
6、文件包含
1.头文件包含
2.嵌套文件包含
问题解决一:
问题解决二:
7、其他预处理指令
一、引言
到这篇文章开始,C语言迎来了最后结束。记住这是书的结束,而不是我们的学习C语言的结束!
但我们要知道C语言的内容不仅仅是这么一点点内容而已,还有很多更加高深,更加底层的知识等着我们去学习,去了解。
C语言的底层逻辑是怎么实现的,代码的执行逻辑又是怎么样的,函数栈帧的创建和销毁,还有编译器提供一系列的库函数等等……
革命尚未成功,同志仍需努力!
二、程序的翻译环境和执行环境
1、什么是程序
计算机程序是一组计算机能识别和执行的指令,运行于电子计算机上,满足人们某种需求的信息
工具。说白了互联网、智能移动设备、云计算、大数据的共同基础、共同的指挥官就是程序。
简而言之,程序=算法+数据结构。
2、程序的翻译环境
在ANSI C(C语言标准)中,存在两个不同的环境,一个是翻译环境,另一个是执行环境。
翻译环境:把程序员写的文本代码转化成机器可识别的二进制指令。
执行环境:说白了就是代码执行所需要的环境,用于代码的执行。
就比如说:文本文件、源文件(test.c)——>通过翻译环境可以转化成可执行文件,也就是二进制文件(test.exe),然后通过执行环境进行执行。
1、test.c通过翻译环境和运行环境进行,来对文件进行可执行文件。
2、翻译环境会包括编译和链接。编译是指编译器(比如VS2019、devc++等),而链接器是链接目标文件和链接库生产的可执行程序(二进制程序)
3、编译又可以分为三个阶段:预编译、编译、汇编。
- 预编译:注释的替换(删除,注释会被替换成一个空格)、头文件的包含(#include<>)、#define符号的替换。这些都会在正式编译前会完成(对文本操作)。
- 编译:把C语言代码翻译成汇编代码。(通过:词法分析,语法分析,语义分析,符号汇总)。
- 汇编:把汇编代码翻译成二进制的指令,生成目标文件(.obj)、生成符号表(汇总的符号都是全局的)。
- 链接器:合并段表、符号表的合并和重定位。
其大体的过程如下:
3、程序的执行环境
程序的执行过程:
1.、程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序
的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
2、程序的执行便开始。接着便调用main函数。
3、 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
4、 终止程序。正常终止main函数;也有可能是意外终止。
三、预处理
1、预定义符号
预定义符号都是语言内置的。
//预定义符号__FILE__ //进行编译的源文件__LINE__ //文件当前的行号__DATE__ //文件被编译的日期__TIME__ //文件被编译的时间__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
#include<stdio.h> int main() {//预定义符号//__FILE__ //进行编译的源文件//__LINE__ //文件当前的行号//__DATE__ //文件被编译的日期//__TIME__ //文件被编译的时间//__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义printf("%s\n", __FILE__);printf("%d\n", __LINE__);printf("%s\n", __DATE__);printf("%s\n", __TIME__);//printf("%d\n", __STDC__);不是所有的编译器都全部按照ANSI C进行编辑的return 0; }
2、#define
1.#define定义标识符
#include<stdio.h>
#define name stuff//语法
#define max 100
#define reg REG
#define do_forever for(;;)
//如果要替换的标识符太长了,可以换行写,但是必须在每行的末尾加上一个反斜杠(最后一行除外)
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
date:%s\ttime:%s\n" ,\
__FILE__,__LINE__ , \
__DATE__,__TIME__ )
int main()
{return 0;
}
注:
- 如果要替换的标识符太长了,可以换行写,但是必须在每行的末尾加上一个反斜杠(最后一行除外)。
- 不要在语句的末尾加上分号。
先存个疑,自己可以进行思考,在代替规则里,我会进行解释。
#include<stdio.h>
#define max 30;
int main()
{//这么写是有语法错误的int n=0;if (max > 0)n -= max;elsen += max;return 0;
}
2.#define定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。
宏的申明方式:
#define name( parament-list ) stuff
其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中。
注意:
参数列表的左括号必须与name紧邻。
如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。
其实就是将#define定义的宏直接往文本文件里面直接进行替换。
文本文件:
#include<stdio.h> #define Add(a,b) ((a)+(b)) int main() {int a = 6;int b = 5;int sum = Add(a, b);printf("%d",sum);return 0; }
被预编译后的文件
#include<stdio.h> #define Add(a,b) ((a)+(b)) int main() {int a = 6;int b = 5;int sum = a+b;printf("%d", sum);return 0; }
3.#define的替换规则
在程序中扩展#define定义符号和宏时,需要涉及几个步骤。
1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
#define的使用规则比较简单,但是需要注意的是#define定义的标识符和宏是完全的遵循替换规则的。(替换规则就是将#define定义的标识符和宏给替换掉)
注意
1、#define定义宏和标识符不是计算好了之后再从文本中找到,然后进行替换。而是先进行替换,然后才计算。
2、宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
3、当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
#define的代替规则。
给标识符max进行#define定义的时候,在30后面多加了一个分号,导致回代的时候会多一个分号。而if——else没有花括号的话,默认只有一条语句,而空语句也算一条语句,导致if——else的语法不符合。
思考题
下面代码的值是多少?
#include<stdio.h> #define c a+b int main() {int a = 3;int b = 5;int ret1 = b * c;int ret2 = c * b;printf("ret1 = %d\nret2 = %d", ret1, ret2);return 0; }
c先会被替换成a+b,然后才开始进行编译。
4.#和##
1.#的作用
如何把宏的参数插入到字符串中?
#define PRINT(FORMAT, VALUE) printf("the value is "#FORMAT"\n", VALUE);//使用 # ,把一个宏参数变成对应的字符串
#include<stdio.h>
int main()
{PRINT("%d", 10);return 0;
}
2.##的作用
##可以把位于它两边的符号合成一个符号。
它允许宏定义从分离的文本片段创建标识符注:
这样的连接必须产生一个合法的标识符。否则其结果就是未定义的
#include<stdio.h>
#define CAT(v,n) v##n
int main()
{int v10 = 100;printf("%d", CAT(v, 10));return 0;
}
5.带副作用的宏参数
这里的副作用不是坏的意思,而是他除了运行这一串代码后,也可能会有什么其他的变化。
例如:
副作用就像下图一样,当a++,b++执行完之后,a和b的值发生了改变。这就叫做“副作用”
#include<stdio.h> #define MAX(a,b) ((a)>(b)?(a):(b)) int main() {int a = 100;int b = 20;int max = MAX(a++, b++);printf("%d", max);return 0; }
6.宏和函数对比
宏的优势
- 宏在一些代码简洁的功能上,时间短。函数写一些简单的功能时,函数调用、函数执行、传返回值会比想象得更加繁琐。
- 函数传参时是有固定的参数类型的,而宏没有这一限制。
函数的优势
- 当代码量大的时候,使用函数更加方便。
- 函数方便调试,宏没办法进行调试。
- 函数更加的严谨。
- 宏可能会带来优先级的问题,而函数不会。
属 性 | #define定义宏 | 函数 |
代 码 长 度 | 每次使用时,宏代码都会被插入到程序中。除了非常 小的宏之外,程序的长度会大幅度增长 | 函数代码只出现于一个地 次使用这个函数时,都调 地方的同一份代码 |
执 行 速 度 | 更快 | 存在函数的调用和返回的 销,所以相对慢一些 |
操 作 符 优 先 级 | 宏参数的求值是在所有周围表达式的上下文环境里, 除非加上括号,否则邻近操作符的优先级可能会产生 不可预料的后果,所以建议宏在书写的时候多些括 号。 | 函数参数只在函数调用的 值一次,它的结果值传递 数。表达式的求值结果更 测。 |
带 有 副 作 用 的 参 数 | 参数可能被替换到宏体中的多个位置,所以带有副作 用的参数求值可能会产生不可预料的结果。 | 函数参数只在传参的时候 次,结果更容易控制。 |
参 数 类 型 | 宏的参数与类型无关,只要对参数的操作是合法的, 它就可以使用于任何参数类型。 | 函数的参数是与类型有关 果参数的类型不同,就需 的函数,即使他们执行的 相同的。 |
调 试 | 宏是不方便调试的 | 函数是可以逐语句调试的 |
递 归 | 宏是不能递归的 | 函数是可以递归的 |
7.命名约定
一般来讲,宏的用法和函数类似,没办法区分。
我们一般全用大写来表示宏名。
不全部为大写来表示函数名。
3、#undef
功能
移除宏定义
#include<stdio.h> #define MAX(a,b) ((a)>(b)?(a):(b)) int main() {int a = 100;int b = 20; #undef MAX//意思就是将名为MAX的宏给移除//int max = MAX(a++, b++);这条语句将会报错,MAX无法解析的外部符号printf("%d", max);return 0; }
4、命令行定义
许多C的编译器都提供了一种能力,可以在命令行中定义符号,用于编译。
5、条件编译
通过条件编译,编译程序的时候,我们将一条语句进行编译和移除很方便。
当#if后面的条件语句真值为1的时候,执行后面的语句,到#endif结束。
#if 1printf("%d", max); #endif
#if 1==1printf("haha"); #elif 2==1printf("hh"); #endif
判断某个宏名是否被定义
//如果SUM宏存在,执行语句;否则不执行//第一种写法 #if defined(SUM) //语句 #endif//第二种写法 #ifdef SUM //语句 #endif
//如果SUM宏不存在,执行语句;否则不执行 //第一种写法 #if !defined(SUM) //语句 #endif//第二种写法 #ifndef SUM //语句 #endif
6、文件包含
1.头文件包含
#include的指令就是在文件里面包含其他文件。
这个不陌生吧,包含标准输入输出文件。
这个包含语句写法有两种:#include<>和#include""。
#include<>(标准库中包含) | #include""(本地文件包含) |
会在标准库中查找,如果没有则报错 | 首先编译器会先在本地文件中进行查找,如果本地文件没有,则会在标准库中查找。如果最终没找到,则报错 |
2.嵌套文件包含
嵌套文件包含会造成文件内容的重复。
问题解决一:
在每个头文件写的时候加上这句话。
#ifndef __TEST_H__ #define __TEST_H__ //头文件的内容 #endif //__TEST_H__
问题解决二:
在头文件的最前面写上这句话。
#pragma once
7、其他预处理指令
#error
#pragma
#line
……
谢谢大家的支持!
相关文章:

C/C++程序设计和预处理
个人主页:仍有未知等待探索_C语言疑难,数据结构,小项目-CSDN博客 专题分栏:C语言疑难_仍有未知等待探索的博客-CSDN博客 目录 一、引言 二、程序的翻译环境和执行环境 1、什么是程序 2、程序的翻译环境 3、程序的执行环境 三、预处理 1、预定义符…...
openssl生成自签名证书
原网址:https://blog.csdn.net/weixin_41767181/article/details/121531007 windows下安装openssl后,生成自签名证书并打包为P12文件的命令如下: 需要注意的是: 每一级的证书中,证书的公司名称等尽量不要一样根证书…...

JAVA毕业设计100—基于Java+Springboot+Vue的WMS仓库管理系统+移动端微信小程序(源码+数据库+部署视频)
基于JavaSpringbootVue的WMS仓库管理系统移动端(源码数据库部署视频) 一、系统介绍 本系统前后端分离带小程序 本系统分为管理员、用户角色(角色权限可自行分配) 功能列表: 1、 数据管理:物料数据管理、物料Bom管理、物料组管理、物料分类管理、供应…...

深度学习推荐系统架构、Sparrow RecSys项目及深度学习基础知识
文章目录 🌟 技术架构:深度学习推荐系统的经典技术架构长啥样?🍊 一、深度学习推荐系统的技术架构🍊 二、基于用户行为的推荐🍊 三、基于多模态数据的推荐🍊 四、基于知识图谱的推荐 dz…...

ios UI 基础开发二
第一节:UIPickerView、UIPickerViewDataSource、UIPickerViewDelegate 设置约束,如果要设置两个兄弟的约束,可以按住option键,用鼠标右键把a拖到b上面,表示a按照b来对齐 生成随机数 如果后面列的数据,依赖前…...
失配树学习笔记
失配树,是一种奇妙的数据结构,它利用 KMP、LCA 解决求两前缀的最长公共 Border 的问题。 首先介绍一下什么是 Border,我们知道 nxt 数组是前后缀相同的最大长度,Border 相当于是 nxt 数组的弱化版,只是去掉了“最大”…...

【Electron】Not allowed to load local resource
问题描述 使用 audio 标签播放音频文件,控制台报错 Not allowed to load local resource。 Not allowed to load local resource原因分析 通常是安全策略所引起的。Electron 默认情况下禁止加载本地资源,以防止潜在的安全风险。 解决方案 在 main.js…...

Maven 基础教程系列
Maven是一个项目开发管理和理解工具。基于项目对象模型的概念:构建、依赖关系管理、文档创建、站点发布和分发发布都由pom.xml声明性文件控制。Maven可以通过插件进行扩展,以使用许多其他开发工具来报告或构建过程。 一、Maven 使用教程-CSDN博客 二、…...

c++之类和对象
1.auto 可以自动推导结果的类型 typeid()可以打印类型 引用也可以 auto真正的价值可以简化迭代器的写法 并且auto定义的变量必须初始化。 不能做参数 返回值也不可以用auto auto不能用来声明数组 如果想要修改要用引用且指针不好解决。 c11之后的nullptr 以后再用空指针用nul…...

分布式应用开发的核心技术系列之——基于TCP/IP的原始消息设计
本文由葡萄城技术团队原创并首发。转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 前言 本文的内容主要围绕以下几个部分: TCP/IP的简单介绍。消息的介绍。基于消息分类的传输格式&…...

医疗领域的数字化浪潮:互联网医院平台的关键作用
数字化浪潮正在迅速改变医疗领域的方式和效率。互联网医院平台作为数字化医疗的关键元素,正在为医疗行业带来巨大的变革。本文将探讨互联网医院平台的关键作用,并提供一个示例,使用Python编写一个简单的医疗预约系统。 互联网医院平台的关键…...

将本地的项目上传到Gitee
目录 1.先在Gitee新建一个仓库,提交即可 2.进入到要上传的项目里面,右键选择 Git Bash Here 3.右键后就打开了Git命令窗口 4.配置你的用户名和邮箱(已经配置过则可跳过) 5.查看你的用户名和邮箱配置(可不查看) 6.输入git init指令&#…...

概率论_概率公式中的分号(;)、逗号(,)、竖线(|)
1. 概率公式中的分号(;)、逗号(,)、竖线(|) ; 分号代表前后是两类东西,以概率P(x;θ)为例,分号前面是x样本,分号后边是模型参数。 , 逗号代表两者地位平等,代表与的关系 | 竖线代表 if,一上面为例,就是如果…...
Spark Streaming 整合 Kafka
本文代码链接:https://download.csdn.net/download/shangjg03/88442308 1.版本说明 Spark 针对 Kafka 的不同版本,提供了两套整合方案:`spark-streaming-kafka-0-8` 和 `spark-streaming-kafka-0-10`,其主要区别如下: 本文使用的 Kafka 版本为 `kafka_2.12-2.2.0`,故采用…...

【API篇】五、Flink分流合流API
文章目录 1、filter算子实现分流2、分流:使用侧输出流3、合流:union4、合流:connect5、connect案例 分流,很形象的一个词,就像一条大河,遇到岸边有分叉的,而形成了主流和测流。对于数据流也一样…...

flutter开发的一个小小小问题,内网依赖下不来
问题 由于众所周知的原因,flutter编译时,经常出现Could not get resource https://storage.googleapis.com/download.flutter.io…的问题,如下: * What went wrong: Could not determine the dependencies of task :app:lintVit…...

RabbitMQ队列及交换机的使用
目录 一、简单模型 1、首先控制台创建一个队列 2、父工程导入依赖 3、生产者配置文件 4、写测试类 5、消费者配置文件 6、消费者接收消息 二、WorkQueues模型 1、在控制台创建一个新的队列 2、生产者生产消息 3、创建两个消费者接收消息 4、能者多劳充分利用每一个消…...
分布式唯一Id,它比GUID好
分布式唯一Id,它比GUID好 一、前言 分布式唯一Id,顾名思义,是指在全世界任何一台计算机上都不会重复的唯一Id。 在单机/单服务器/单数据库的小型应用中,不需要用到这类东西。但在高并发、海量数据、大型分布式应用中,…...

计算机服务器中了勒索病毒怎么解决,勒索病毒解密流程,数据恢复
计算机服务器中了勒索病毒是一件非常令人头疼的事情,勒索病毒不仅会加密企业服务器中的数据,还会对企业计算机系统带来损害,严重地影响了企业的正常运转。最近,云天数据恢复中心工程师总结了,今年以来网络上流行的勒索…...

【NPM】vuex 数据持久化库 vuex-persistedstate
在 GitHub 上找到:vuex-persistedstate。 安装 npm install --save vuex-persistedstate使用 import { createStore } from "vuex"; import createPersistedState from "vuex-persistedstate";const store createStore({// ...plugins: [cr…...
C/Python/Go示例 | Socket Programing与RPC
Socket Programming介绍 Computer networking这个领域围绕着两台电脑或者同一台电脑内的不同进程之间的数据传输和信息交流,会涉及到许多有意思的话题,诸如怎么确保对方能收到信息,怎么应对数据丢失、被污染或者顺序混乱,怎么提高…...

【自然语言处理】大模型时代的数据标注(主动学习)
文章目录 A 论文出处B 背景B.1 背景介绍B.2 问题提出B.3 创新点 C 模型结构D 实验设计E 个人总结 A 论文出处 论文题目:FreeAL: Towards Human-Free Active Learning in the Era of Large Language Models发表情况:2023-EMNLP作者单位:浙江大…...

基于python大数据的水文数据分析可视化系统
博主介绍:高级开发,从事互联网行业六年,熟悉各种主流语言,精通java、python、php、爬虫、web开发,已经做了多年的设计程序开发,开发过上千套设计程序,没有什么华丽的语言,只有实实在…...

C# winform教程(二)----button
一、button的使用方法 主要使用方法几乎都在属性内,我们操作也在这个界面 二、作用 用户点击时触发事件,事件有很多种,可以根据需要选择。 三、常用属性 虽然属性很多,但是常用的并不多 3.常用属性 名称内容含义AutoSize自动调…...

第22讲、Odoo18 QWeb 模板引擎详解
Odoo QWeb 模板引擎详解与实战 Odoo 的 QWeb 是其自研的模板引擎,广泛应用于 HTML、XML、PDF 等内容的生成,支撑了前端页面渲染、报表输出、门户页面、邮件模板等多种场景。本文将系统介绍 QWeb 的核心用法、工作原理,并通过实战案例演示如何…...

MSYS2 环境配置与 Python 项目依赖管理笔记
#工作记录 MSYS2 环境配置 安装和更新 MSYS2 初始安装 下载并安装 MSYS2: 访问 MSYS2 官方网站 并下载安装包。 按照安装向导完成安装。 更新 MSYS2: 打开 MSYS2 终端(MSYS2 MINGW64)。 更新包数据库和核心系统包࿱…...
【Dv3Admin】系统视图下载中心API文件解析
大文件导出与批量数据下载常常成为后台系统性能瓶颈,合理管理下载任务是保障系统稳定运行的关键。任务化下载机制通过异步处理,避免前端等待阻塞,提升整体交互体验。 围绕 download_center.py 模块,剖析其在下载任务创建、查询、…...

低代码逻辑引擎配置化实战:三步穿透审批记录查询
在堆积如山的报销单中埋头寻找某笔特殊费用的审批轨迹在跨部门协作时被追问"这个合同到底卡在哪个环节" 在快节奏的办公自动化场景中,这些场景是很常见的,传统OA系统中分散的审批记录查询方式往往太繁琐。 为破解这一痛点,在JVS低…...

AUTOSAR实战教程--标准协议栈实现DoIP转DoCAN的方法
目录 软件架构 关键知识点 第一:PDUR的缓存作用 第二:CANTP的组包拆包功能 第三:流控帧的意义 配置过程 步骤0:ECUC模块中PDU创建 步骤1:SoAD模块维持不变 步骤2:DoIP模块为Gateway功能添加Connection 步骤3:DoIP模块为Gateway新增LA/TA/SA 步骤4:PDUR模…...

汽车车载软件平台化项目规模颗粒度选择的一些探讨
汽车进入 SDV 时代后,车载软件研发呈现出开源生态构建、电子架构升级、基础软件标准化、本土供应链崛起、AI 原生架构普及、云边协同开发等趋势,这些趋势促使车载软件研发面临新挑战,如何构建适应这些变化的平台化架构成为车企与 Tier 1 的战…...