C语言程序的编译与预处理详解

目录
  • 一、程序的编译
    • 1、 编译阶段
    • 2、链接
  • 二、预处理详解
    • 1、预定义符号
    • 2、#define定义的标识符
    • 3、#define定义的宏
    • 4、#unef
  • 总结

一、程序的编译

我们写的源文件(*.c)是经过怎样的处理生产可执行文件(*.exe)的呢?这种处理有两个步骤—编译和链接。源文件在编译阶段通过编译器将每个源文件转换为目标文件(这些文件是可执行的机器指令),再通过链接器将其捆绑到一起,生成一个完整的可执行程序。

1、 编译阶段

编译阶段可细分为3个阶段:预处理(即预编译)、编译、汇编

预处理:主要进行头文件的包含、处理预处理指令(如:#define定义符号的替换)、删除注释。此时(*.c)文件转换为(*.i)文件。

编译:主要进行语法分析、词法分析、语义分析、符号汇总,此阶段会将c语言代码转换为汇编代码。此时(*.i)文件转换为(*.s)文件。

汇编:此阶段会形成符号表,将汇编代码转换为二进制指令(机器指令)。此时(*.s)文件转换为(*.o)文件。

2、链接

此阶段主要完成合并段表、符号表的合并和重定位工作,将(*.o)文件链接到一起生成可执行文件。

二、预处理详解

1、预定义符号

__FILE__      //进行编译的源文件
__LINE__     //文件当前的行号
__DATE__    //文件被编译的日期
__TIME__    //文件被编译的时间
__STDC__    //如果编译器遵循ANSI C,其值为1;如果不遵循,则编译器不认识这个符号,是个未定义标识符,此时使用这个符号程序会报错。该符号的作用是判断编译器是否遵循ANSI C。

下面为大家展示这些符号的作用。

执行结果为:

2、#define定义的标识符

语法:

#define MAX 100

这句代码的意思是数字“100”替换成符号MAX,例如:

#include <stdio.h>
#define MAX 100
int main()
{
	int i = 1 + MAX;
	printf("%d", i);
	return 0;
}

结果:

注意:define定义的标识符只进行替换,并不会在替换过程中执行运算,例如

#include <stdio.h>
#define MAX 5+5
int main()
{
	int i = 10;
	int x = i * MAX;
	printf("%d", x);
	return 0;
}

这个程序的结果是多少呢?100吗?答案其实是55,因为define只是简单的替换,i*MAX其实本质是i*5+5,即10*5+5=55.

3、#define定义的宏

语法:

#define SQUARE(x) x * x

当我们在程序中写下:SQUARE(5)这句代码,这预处理时,这句代码就会被替换成5*5。例如:

#include <stdio.h>
#define SQUARE(x) (x) * (x)
int main()
{
	int i = SQUARE(5);
	printf("%d", i);
	return 0;
}

程序结果为:

注意:同样的#define定义的宏也只是进行简单的替换。

例如:

#include <stdio.h>
#define SQUARE(x) x * x
int main()
{
	int a = 5;
	int i = SQUARE(a+1);
	printf("%d", i);
	return 0;
}

这个程序的结果是什么呢?答案是11,SQUARE(a+1)等同于SQUARE(5+1),即5+1*5+1=11,而不是6*6=36。

宏常常被用于执行简单的运算,比如在两个数中找出较大的一个。

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

那么为什么不用函数呢,因为宏有两个优势:

1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序 的规模和速度方面更胜一筹。

2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可 以适用于整形、长整型、浮点型等可以用于>来比较的类型。宏是类型无关的。

但宏也有劣势:

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

2. 宏是没法调试的。

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

4. 宏可能会带来运算符优先级的问题,导致程容易出现错。(就是上面我提醒大家需要注意的地方)

4、#unef

这条指令用于移除一个#define定义标识符或宏定义。

例如:

#define	MAX 100
int main()
{
	int m = MAX;
#undef MAX
	int n = MAX;//err
	return 0;
}

这个程序会报错,因为那个int n=MAX中的MAX的定义被移除了,而其他的不会(m仍然是正确的)。

总结

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

(0)

相关推荐

  • C语言预处理预编译命令及宏定义详解

    目录 程序翻译环境和执行环境 翻译环境:详解编译+链接 1. 编译 - 预处理/预编译 test.c ---- test.i 2. 编译 - 编译 test.i ---- test.s 3. 编译 - 汇编 test.s ---- test.obj 4. 链接 test.obj ---- test.exe 运行环境 预处理/预编译详解 #define 定义标识符 #和## #的作用 ##的作用 命名约定 命令行定义 条件编译 常见的条件编译指令 文件包含 offsetof(宏类型,成员名字)偏移

  • 详解C语言编程中预处理器的用法

    预处理最大的标志便是大写,虽然这不是标准,但请你在使用的时候大写,为了自己,也为了后人. 预处理器在一般看来,用得最多的还是宏,这里总结一下预处理器的用法. #include <stdio.h> #define MACRO_OF_MINE #ifdef MACRO_OF_MINE #else #endif 上述五个预处理是最常看见的,第一个代表着包含一个头文件,可以理解为没有它很多功能都无法使用,例如C语言并没有把输入输入纳入标准当中,而是使用库函数来提供,所以只有包含了stdio.h这个头文

  • 详解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预处理宏定义

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

  • 详解C语言之预处理

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

  • C语言预处理详解

    目录 一,预定义符号 二,#define 1,#define 定义标识符 2,#define 定义宏 3,#define 替换规则 三,##的作用 1,概念 2,带副作用的宏参数 3,宏和函数对比 四,命名约定 1,#undef 2,文件包含 总结 一,预定义符号 __FILE__ //进行编译的源文件 __LINE__ //文件当前的行号 __DATE__ //文件被编译的日期 __TIME__ //文件被编译的时间 __STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义 in

  • C语言程序的编译与预处理详解

    目录 一.程序的编译 1. 编译阶段 2.链接 二.预处理详解 1.预定义符号 2.#define定义的标识符 3.#define定义的宏 4.#unef 总结 一.程序的编译 我们写的源文件(*.c)是经过怎样的处理生产可执行文件(*.exe)的呢?这种处理有两个步骤-编译和链接.源文件在编译阶段通过编译器将每个源文件转换为目标文件(这些文件是可执行的机器指令),再通过链接器将其捆绑到一起,生成一个完整的可执行程序. 1. 编译阶段 编译阶段可细分为3个阶段:预处理(即预编译).编译.汇编 预

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

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

  • C语言程序的编译与预处理基础定义讲解

    目录 程序的翻译环境和执行环境 1.翻译环境 2.运行环境 预处理详解 预定义符号 #define #define定义宏 #define替换规则 #和## 带副作用的宏参数 宏和函数对比 命名约定 #undef 命令行定义 条件编译 文件包含 程序的翻译环境和执行环境 在ANSIC的任何一种实现中,存在两个不同的环境:翻译环境和执行环境 翻译环境:源代码被转换为可执行的机器指令. 执行环境:实际执行代码. 1.翻译环境 组成一个程序的每个源文件通过编译分别转换成目标文件(object code)

  • Go语言程序查看和诊断工具详解

    想必Java 的开发者没有不知道或者没用过 jps 这个命令的,这个命令是用来在主机上查看有哪些 Java 程序在运行的. 我刚用 Go 语言程序的时候也很苦恼,我部署在公司服务器上的 Go 程序,其他的同事由于不清楚就经常找不到. 那么 Go 语言有没有像 jps 这样的工具呢?当然有,不仅有,而且还是 Google 自己出品的,官方认证(这种问题 Google 不可能自己想不到啊).名称也跟 jps 很像,叫 gops. 安装 gops 并不包含在官方安装包中,不属于标准工具.需要手动获取.

  • 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 替换规则 #和##两个预处理的工具 带副作用的宏参数 宏和函数对比 #undef移除宏 命令行定义 条件编译 头文件包含 嵌套文件包含 总结 一.程序的翻译环境和运行环境 重点:任何ANSI C(标准C的程序)的一种实现,存在两个不同的环境 第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令. 第2种是执行环境,

  • C语言深入探究程序的编译之预处理

    目录 1.程序的翻译环境和执行环境 2.详解编译与链接 2.1翻译环境 2.2编译本身也分为几个阶段 2.3运行环境 3.预处理详解 3.1预处理符号 3.2#define 3.2.1#define定义标识符 3.2.2#define定义宏 3.2.3#define的替换规则 3.2.4 宏和函数对比 3.2.5命名约定 3.3#undef 3.4条件编译 1.程序的翻译环境和执行环境 在ANSI C中任何一种实现中,存在两个不同的环境. 第一种是翻译环境,在这个环境中源代码被转换为可执行的机器

  • C语言程序环境编译+链接理论

    目录 一.程序的翻译环境(编译和链接) 二.程序的运行环境 一.程序的翻译环境(编译和链接) 在ANSI C 的任何一种实现中,存在两个不同的环境: 第一种是翻译环境,在这个环境中源代码被转换成可执行的机器指令. 第二种是执行环境,它用于实际执行代码. 如下图:就是我们编译器编译一个源文件到一个可执行文件的大致过程 组成一个程序的每个源文件(test.c)通过编译过程分别转换成目标代码(test.obj) 每个目标文件又由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序. 链接器

  • C语言中的正则表达式使用示例详解

    正则表达式,又称正规表示法.常规表示法(英语:Regular Expression,在代码中常简写为regex.regexp或RE).正则表达式是使用单个字符串来描述.匹配一系列符合某个句法规则的字符串. 在c语言中,用regcomp.regexec.regfree 和regerror处理正则表达式.处理正则表达式分三步: 编译正则表达式,regcomp: 匹配正则表达式,regexec: 释放正则表达式,regfree. 函数原型 /* 函数说明:Regcomp将正则表达式字符串regex编译

  • C语言switch使用之诡异用法详解

    关于switch的用法这里不再做什么总结了,其实这个是一个便捷的快速跳转条件切换器.而关于这个功能最常用的技术讨论点在于case后面的break以及default.这里不讨论这些,直接看下面的代码: #include"stdio.h" int main(void) { int num = 0; switch(num) { printf("functionrun!\n"); } return 0; } 上面的代码中用到了一个switch,但是代码块中没有任何case以

随机推荐