详解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

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • C语言的程序环境与预处理你真的了解吗

    目录 1.翻译环境 2.运行环境 3.预处理详解 3.1#define定义的符号 3.2#define定义的宏 3.3#define的替换规则 3.4#与## 4.宏与函数对比 5.#undef 6.条件编译 7.文件包含 总结 c语言代码的实现包含两种环境 1.翻译环境,将源代码转化成可执行的机器指令 2.执行环境,执行代码 1.翻译环境 包括两个过程,编译与链接 程序中每一个源文件通过编译器转化成目标文件(obj) 这些目标文件又通过链接器捆绑在一起 链接器同时会链接标准库中的函数以及程序员

  • C语言程序环境中的预处理详解

    目录 一.翻译环境 二.执行环境 三.预处理 1.预处理符号 2.#define定义标识符 3.#define定义宏 4.#和## 5.宏和函数的对比 6.条件编译 7.文件包含 总结 一.翻译环境 整个翻译环境大致就可以画成这样一张图. 下列有几点需要说明: 1. 组成一个程序的每一个源文件通过编译过程分别转换成目标文件(在Linux中目标文件的后缀为.o:而在Windows中目标文件后缀为.obj) 2. 每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序 3.

  • c语言的程序环境与预处理详解

    目录 1.翻译环境 2.运行环境 3.预处理详解 3.1#define定义的符号 3.2#define定义的宏 3.3#define的替换规则 3.4#与## 4.宏与函数对比 5.#undef 6.条件编译 7.文件包含 总结 c语言代码的实现包含两种环境 1.翻译环境,将源代码转化成可执行的机器指令 2.执行环境,执行代码 1.翻译环境 包括两个过程,编译与链接·程序中每一个源文件通过编译器转化成目标文件(obj)·这些目标文件又通过链接器捆绑在一起·链接器同时会链接标准库中的函数以及程序员

  • 详解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语言的预处理介绍

    目录 前言 一.预定义符号 二.#define 1.宏 2.宏与函数 3.带副作用的宏参数 4. 宏和函数的不同 5.#undef 三.条件编译 四.文件包含 总结 前言 编译一个C语言程序涉及很多步骤.其中第一个步骤被称为预处理.C语言的预处理器在源代码编译之前对其进行一些文本性质的操作.它的主要任务包括删除注释.插入被#include指令包含的文件内容.定义和替换由#define指令定义的符号,同时确定代码的部分内容是否应该根据一些条件编译指令进行编译. 一.预定义符号 下表为C语言预处理器

  • 详解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宏定义无参的一般形式为:#define  标识符 常量 #define宏定义有参的一般形式为:#define  标识符(参数表) 表达式 #运算符: ##运算符: 可变宏...和__VA_ARGS__: 开发项目中常用的宏定义: #define介绍: C语言里可以用#define定义一个标识符来表示一个常量.特点是:定义的标识符不占内存,只是一个临时的符号,预编译后这个符号就不存在了,也不做类型定义.预编译又叫预处理.预编译就是编译前的处理.这个操作是在

  • 详解C语言之预处理

    目录 程序的翻译环境 编译 预编译: 编译: 汇编: 链接 合并段表: #define的用法 1.#define定义标识符,例如 2.#define定义宏 3.#define实现将参数插入到字符串中 总结 程序的翻译环境 源文件被转换成可执行的机器指令时所处的环境称为翻译环境. 由源文件(.c)转换成可执行文件(.exe)需要两步 编译通过编译器实现,链接通过链接器实现 每个源文件都会经过编译器处理后生成对应的目标文件,然后链接器将目标文件和链接库链接在一起生成可执行程序 编译和链接的具体操作

  • 详解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语言 三大循环 四大跳转 和判断语句

    三大循环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语言进程同步机制

    本文是对进程同步机制的一个大总结(9000+字吐血总结),涵盖面非常的全,包括了进程同步的一些概念.软件同步机制.硬件同步机制.信号量机制和管程机制,对每种机制结合代码做了详细的介绍,并且对琐碎的知识点和概念解释的非常清晰. ​ 在前面的博客中讲述了进程的状态及其状态的转换,每种状态的含义和转换的原因.同样我们也知道,在OS引入了进程后,可以使系统中的多道程序可以并发的执行,进程的并发执行一方面极大的提高了系统的资源利用率和吞吐量,但是另一方面却使系统变得更加复杂,如果不能采取有效的措施,对多个

  • 详解Go语言中关于包导入必学的 8 个知识点

    1. 单行导入与多行导入 在 Go 语言中,一个包可包含多个 .go 文件(这些文件必须得在同一级文件夹中),只要这些 .go 文件的头部都使用 package 关键字声明了同一个包. 导入包主要可分为两种方式: 单行导入 import "fmt" import "sync" 多行导入 import( "fmt" "sync" ) 如你所见,Go 语言中 导入的包,必须得用双引号包含,在这里吐槽一下. 2. 使用别名 在一些场

随机推荐