详解C语言之预处理
目录
- 程序的翻译环境
- 编译
- 预编译:
- 编译:
- 汇编:
- 链接
- 合并段表:
- #define的用法
- 1.#define定义标识符,例如
- 2.#define定义宏
- 3.#define实现将参数插入到字符串中
- 总结
程序的翻译环境
源文件被转换成可执行的机器指令时所处的环境称为翻译环境。
由源文件(.c)转换成可执行文件(.exe)需要两步
编译通过编译器实现,链接通过链接器实现
每个源文件都会经过编译器处理后生成对应的目标文件,然后链接器将目标文件和链接库链接在一起生成可执行程序
编译和链接的具体操作
编译
编译分为预编译、编译和汇编
预编译:
1.#include<>头文件的包含,即将头文件的引用替换为函数具体的声明。
2.删除注释
3.#define,预处理操作,将define定义的替换为实际值
编译:
将C语言代码翻译为汇编代码
语法分析,词法分析,语义分析,符号汇总(函数名,全局变量)
汇编:
将汇编代码转换为二进制代码,形成符号表
链接
链接分为合并段表和符号表的合并和符号的重定位
合并段表:
目标文件都有一定的格式,分为几个段。链接器会将目标文件的相同的段里的数据合并到一起。
符号表的合并和符号的重定位:
链接器会将符号表合并为一张表,合并后当符号有冲突时,无效地址将被重新定位为有效地址,即合并后
链接操作完成后可执行程序就生成了
#define的用法
1.#define定义标识符,例如
define定义标识符时最好不要加分号";" 否则容易导致语法错误
2.#define定义宏
宏的申明方式:
#define name(parament-list) stuff
其中parament-list是由逗号隔开的符号表,可能出现在stuff中
注意:参数的左括号必须与name相邻
下面通过一些代码来看看使用宏时需要注意的问题
输出结果为11,而非36。问题出在哪呢,我们要明确函数和宏的区别,函数是传递参数的值,而宏是替换参数。#define的实质就是替换。
所以X会替换成表达式5 + 1,5+1*5+1结果显然为11。如果要改进的话在使用宏时可以多加括号,不要吝啬括号
再看另一个例子
我们发现结果依然不是我们想象那样为100,而是55。要知道不仅参数是替换,整个式子也是替换的。则DOUBLE(5)会替换成 (5) + ( 5),那么10*(5)+(5)结果为55,如果要改进的话则依然是加括号
所以记住一点,使用宏的时候要不要吝啬括号。
3.#define实现将参数插入到字符串中
实现如下 #会将X变成字符串"X"
输出结果:
hello aworld
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!
相关推荐
-
详解C语言编程中预处理器的用法
预处理最大的标志便是大写,虽然这不是标准,但请你在使用的时候大写,为了自己,也为了后人. 预处理器在一般看来,用得最多的还是宏,这里总结一下预处理器的用法. #include <stdio.h> #define MACRO_OF_MINE #ifdef MACRO_OF_MINE #else #endif 上述五个预处理是最常看见的,第一个代表着包含一个头文件,可以理解为没有它很多功能都无法使用,例如C语言并没有把输入输入纳入标准当中,而是使用库函数来提供,所以只有包含了stdio.h这个头文
-
你必须知道的C语言预处理的问题详解
C语言预处理器执行宏替换.条件编译和文件包含.通常采用以"#"为行首的提示.下面是C语言预处理的应用场合: 1.三字母词(Trigraph Sequences) C源程序的字符集被包含在7位的ASCII字符集中,但是它是ISO 646-1983 Invariant Code Set的超集.为了让程序可以在缩减集(reduced set)中呈现出来,下面的三字母词会被替换成相应的单字符. 三字母词 单字符 ??= # ??/ \ ??' ^ ??( [ ??) ] ??! | ??<
-
C语言程序的编译与预处理详解
目录 一.程序的编译 1. 编译阶段 2.链接 二.预处理详解 1.预定义符号 2.#define定义的标识符 3.#define定义的宏 4.#unef 总结 一.程序的编译 我们写的源文件(*.c)是经过怎样的处理生产可执行文件(*.exe)的呢?这种处理有两个步骤-编译和链接.源文件在编译阶段通过编译器将每个源文件转换为目标文件(这些文件是可执行的机器指令),再通过链接器将其捆绑到一起,生成一个完整的可执行程序. 1. 编译阶段 编译阶段可细分为3个阶段:预处理(即预编译).编译.汇编 预
-
C语言预处理预编译命令及宏定义详解
目录 程序翻译环境和执行环境 翻译环境:详解编译+链接 1. 编译 - 预处理/预编译 test.c ---- test.i 2. 编译 - 编译 test.i ---- test.s 3. 编译 - 汇编 test.s ---- test.obj 4. 链接 test.obj ---- test.exe 运行环境 预处理/预编译详解 #define 定义标识符 #和## #的作用 ##的作用 命名约定 命令行定义 条件编译 常见的条件编译指令 文件包含 offsetof(宏类型,成员名字)偏移
-
详解C语言#define预处理宏定义
目录 #define介绍: #define宏定义无参的一般形式为:#define 标识符 常量 #define宏定义有参的一般形式为:#define 标识符(参数表) 表达式 #运算符: ##运算符: 可变宏...和__VA_ARGS__: 开发项目中常用的宏定义: #define介绍: C语言里可以用#define定义一个标识符来表示一个常量.特点是:定义的标识符不占内存,只是一个临时的符号,预编译后这个符号就不存在了,也不做类型定义.预编译又叫预处理.预编译就是编译前的处理.这个操作是在
-
C语言预处理详解
目录 一,预定义符号 二,#define 1,#define 定义标识符 2,#define 定义宏 3,#define 替换规则 三,##的作用 1,概念 2,带副作用的宏参数 3,宏和函数对比 四,命名约定 1,#undef 2,文件包含 总结 一,预定义符号 __FILE__ //进行编译的源文件 __LINE__ //文件当前的行号 __DATE__ //文件被编译的日期 __TIME__ //文件被编译的时间 __STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义 in
-
详解C语言之预处理
目录 程序的翻译环境 编译 预编译: 编译: 汇编: 链接 合并段表: #define的用法 1.#define定义标识符,例如 2.#define定义宏 3.#define实现将参数插入到字符串中 总结 程序的翻译环境 源文件被转换成可执行的机器指令时所处的环境称为翻译环境. 由源文件(.c)转换成可执行文件(.exe)需要两步 编译通过编译器实现,链接通过链接器实现 每个源文件都会经过编译器处理后生成对应的目标文件,然后链接器将目标文件和链接库链接在一起生成可执行程序 编译和链接的具体操作
-
详解C语言的预处理效果
目录 前言 一.预定义符号 二.#define 1.宏 2.宏与函数 3.带副作用的宏参数 4. 宏和函数的不同 5.#undef 三.条件编译 四.文件包含 1.函数库文件包含 2.本地文件包含 总结 前言 编译一个C语言程序涉及很多步骤.其中第一个步骤被称为预处理.C语言的预处理器在源代码编译之前对其进行一些文本性质的操作.它的主要任务包括删除注释.插入被#include指令包含的文件内容.定义和替换由#define指令定义的符号,同时确定代码的部分内容是否应该根据一些条件编译指令进行编译.
-
详解C语言之预处理(上)
目录 程序的翻译环境 编译 预编译: 编译: 汇编: 链接 合并段表: #define的用法 1.#define定义标识符,例如 2.#define定义宏 3.#define实现将参数插入到字符串中 总结 程序的翻译环境 源文件被转换成可执行的机器指令时所处的环境称为翻译环境. 由源文件(.c)转换成可执行文件(.exe)需要两步 编译通过编译器实现,链接通过链接器实现 每个源文件都会经过编译器处理后生成对应的目标文件,然后链接器将目标文件和链接库链接在一起生成可执行程序 编译和链接的具体操作
-
详解C语言之预处理(下)
目录 #define定义宏带副作用的宏参数 #define定义宏的优点 #define定义宏劣势 预处理 预定义符号 预处理指令 条件编译 1.调试性代码 2.防止重复的头文件多次编译 总结 #define定义宏带副作用的宏参数 我们来看如下一段代码 结果分别为12,11,13 当参数替换后,首先判断表达式 (a++)>(b++)?,判断后a的值加1 b的值加1,然后执行表达式(b++)此时执行的值为12,执行完成后b的值加1,则a的值为11,b的值为13.可以看出对于这种情况下的宏是带有副作用
-
详解C 语言项目中.h文件和.c文件的关系
详解C 语言项目中.h文件和.c文件的关系 在编译器只认识.c(.cpp))文件,而不知道.h是何物的年代,那时的人们写了很多的.c(.cpp)文件,渐渐地,人们发现在很多.c(.cpp)文件中的声明语句就是相同的,但他们却不得不一个字一个字地重复地将这些内容敲入每个.c(.cpp)文件.但更为恐怖的是,当其中一个声明有变更时,就需要检查所有的.c(.cpp)文件. 于是人们将重复的部分提取出来,放在一个新文件里,然后在需要的.c(.cpp)文件中敲入#include XXXX这样的语句.这样即
-
详解C语言结构体,枚举,联合体的使用
目录 一.匿名结构体 二.结构体的自引用 1.声明时不要自己引用自己 2.结构体重命名时不能使用重命名 三.结构体内存对齐规则 1.结构体内存计算 2.结构体嵌套 3.通过调整结构体成员顺序,压缩内存 四.存在内存对齐的原因 五.修改默认对齐数 六.结构体传参 七.位段 1.位段在内存中的存储 2.位段的跨平台问题 八.枚举 1.枚举的定义 2.枚举的优点 九.联合体(共用体) 1.联合体大小的计算 2.使用联合体判断计算机的大小端字节序 一.匿名结构体 struct { char name[2
-
详解C语言函数返回值解析
详解C语言函数返回值解析 程序一: int main() { int *p; int i; int*fun(void); p=fun(); for(i=0;i<3;i++) { printf("%d\n",*p); p++; } return 0; }; int* fun(void) { static int str[]={1,2,3,4,5}; int*q=str; return q; } //不能正确返回 虽然str是在动态变量区,而该动态变量是局部的,函数结束时不保留的.
-
详解C语言 三大循环 四大跳转 和判断语句
三大循环for while 和 do{ }while; 四大跳转 : 无条件跳转语句 go to; 跳出循环语句 break; 继续跳出循环语句 continue; 返回值语句 return 判断语句 if,if else,if else if else if...else ifelse 组合 if(0 == x) if(0 == y) error(): else{ //program code } else到底与那个if配对 C语言有这样的规定: else 始终与同一括号内最近的未匹配的if语
-
详解C语言gets()函数与它的替代者fgets()函数
在c语言中读取字符串有多种方法,比如scanf() 配合%s使用,但是这种方法只能获取一个单词,即遇到空格等空字符就会返回.如果要读取一行字符串,比如: I love BIT 这种情况,scanf()就无能为力了.这时我们最先想到的是用gets()读取. gets()函数从标准输入(键盘)读入一行数据,所谓读取一行,就是遇到换行符就返回.gets()函数并不读取换行符'\n',它会吧换行符替换成空字符'\0',作为c语言字符串结束的标志. gets()函数经常和puts()函数配对使用,puts
随机推荐
- MySQL——修改root密码的4种方法(以windows为例)
- Flex 自定义DataGrid实现根据条目某一属性值改变背景颜色
- 在JScript中使用缓存技术的实际代码
- GO语言实现文件上传代码分享
- 分享精心挑选的23款美轮美奂的jQuery 图片特效插件
- Java中Map的排序问题详解
- asp提取内容中的手机号码,qq,网址的正则代码
- Oracle触发器实例代码
- DB2数据库切换为oracle数据库经验教训总结(必看篇)
- javascript实现表格排序 编辑 拖拽 缩放
- BootStrap初学者对弹出框和进度条的使用感觉
- php中文验证码实现示例分享
- 正则表达式笔记三则
- jQuery中选择器的基础使用教程
- 解读赫夫曼树编码的问题
- php中通过eval实现字符串格式的计算公式
- php _autoload自动加载类与机制分析
- 解析php 版获取重定向后的地址(代码)
- centOS7下Spark安装配置教程详解
- vue二级路由设置方法