前言
本篇文章讲述了程序的翻译环境和执行环境,编译、连接,预定义符号,#define,#符号和##符号的相关知识。
文章目录
- 前言
- 1.程序的翻译环境和执行环境
- 2.编译+链接
- 2.1 翻译环境
- 2.2 运行环境
- 3.预处理详解(各预处理符号使用说明)
- 3.1 预定义符号
- 3.2 #define
- 3.2.1 #define 替换规则
- 3.3 #符号
- 3.4 ##符号
1.程序的翻译环境和执行环境
在ANSI C的任何一种实现中,都存在两个不同的环境。
第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。
第2种是执行环境,它用于实际执行代码。
2.编译+链接
2.1 翻译环境
①组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)。
②每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
③链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人 的程序库,将其需要的函数也链接到程序中。
编译过程如下:
再具体来说可以分为如下步骤:
- 预处理 选项 gcc -E test.c -o test.i
预处理完成之后就停下来,预处理之后产生的结果都放在test.i文件中。- 编译 选项 gcc -S test.c
编译完成之后就停下来,结果保存在test.s中。- 汇编 gcc -c test.c
汇编完成之后就停下来,结果保存在test.o中。
其具体过程我们不必深究,只需要了解其如何工作,其过程是什么样子即可。
2.2 运行环境
程序执行的过程:
- 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序 的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
- 程序的执行便开始。接着便调用main函数。
- 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回 地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程 一直保留他们的值。
- 终止程序。正常终止main函数;也有可能是意外终止。
3.预处理详解(各预处理符号使用说明)
3.1 预定义符号
FILE //进行编译的源文件
LINE //文件当前的行号
DATE //文件被编译的日期
TIME //文件被编译的时间
FUNCTION//文件被编译的函数
STDC //如果编译器遵循ANSI C,其值为1,否则未定义
使用举例:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{printf("%s\n", __FILE__);printf("%d\n", __LINE__);printf("%s\n", __DATE__);printf("%s\n", __TIME__);printf("%s\n", __FUNCTION__);//printf("%d\n", __STDC__);//当前使用是的VS2019不遵循ANSI C//运行不出来,故注释掉return 0;
}
3.2 #define
语法:
#define name stuff
使用举例:
#define MAX 100//定义MAX为100
#define STR "abcdef"//定义STR为字符串abcdef
#define INT int//定义INT为int类型
提示:在define定义标识符的时候,要不要在最后加上 ; ?
千万不要加,加了会出问题
比如:
#define MAX 100;
int main()
{int a = MAX;int b = 0;if (a > 5)b = MAX;elseb = -1;printf("%d\n", MAX);return 0;
}
这是因为define只是一个宏定义,他在后面用的时候是原封不动的替换过去。
int a=MAX;就相当于int a=1000;;所以会出问题。
再比如:
#define ADD 2+3
int main()
{
printf("%d",3*ADD);
return 0;
}
其结果为9,就是因为只是替换过去3*2+3而已。
3.2.1 #define 替换规则
在程序中扩展#define定义符号和宏时,需要涉及几个步骤。
- 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先 被替换。
- 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
- 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
注意:
- 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
- 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
3.3 #符号
使用 # ,把一个宏参数变成对应的字符串
int main()
{int a = 10;printf("the value of a is %d\n", a);int b = 20;printf("the value of b is %d\n", b);float f = 4.5f;printf("the value of f is %f\n", f);return 0;
}
我们知道这样可以打印出数字,但是我们如果想利用宏来打印数字该怎么办呢?
#define PRINT(n, format) printf(“the value of " " is “format”\n”,n)
这样利用宏定义虽然可以打印出来,但是我们的名字无法一一对应上,这时候我们的#就起作用了。#define PRINT(n, format) printf(“the value of “#n” is “format”\n”, n)
我们修改宏定义为这样
#define PRINT(n, format) printf("the value of "#n" is "format"\n", n)
int main()
{int a = 10;PRINT(a, "%d");int b = 20;PRINT(b, "%d");float f = 4.5f;PRINT(f, "%f");return 0;
}
这样我们利用#和宏定义就可以打出和printf一样的效果了。
再比如:
int i = 10;
#define PRINT(FORMAT, VALUE)\
printf("the value of " #VALUE "is "FORMAT "\n", VALUE);
...
PRINT("%d", i+3);//产生了什么效果?
代码中的 #VALUE 会预处理器处理为:
“VALUE” .
最终的输出的结果应该是:
the value of i+3 is 13
3.4 ##符号
##可以把位于它两边的符号合成一个符号。
它允许宏定义从分离的文本片段创建标识符。
#define CAT(v, n) v##nint main()
{int value10 = 100;printf("%d\n", CAT(value, 10));printf("%d\n", value10);return 0;
}
#define CAT(v, n) v##n
CAT(value, 10)
##就相当于把value和10这两边的符号合成一个符号。
所以CAT(value, 10)==value10