目录
1.程序的翻译环境和执行环境
2.详解编译+链接
(1)翻译环境
整体过程:
每步详解:
这里来讲解一下符号汇总,形成符号表,符号表的合并和重定位三大过程;
符号汇总:
汇总的都是全局的符号.
形成符号表,符号表的合并和重定位:
看这个例子:
一个加法函数分两个源文件来写,在汇编过程中会形成各自的符号表,以上例子中Add.c中定义了add函数,在Add.o中会形成自己的符号表,并且有它的地址;test.c文件中使用extern声明了Add函数,test.o中会形成main函数的符号表和地址,Add的符号表和地址;在进行符号表合并时,test.o中Add的地址是无效的,因为它只是声明没有定义,所以符号表合是合并main和Add.o中Add的符号表。
由此引出了以下的一种错误,只声明不定义:
这是一种链接型错误
(2)运行环境
3.预处理详解
(1)预定义符号
这些预定义符号都是语言内置的(VS编译器是不遵循ANSI C的)
int main()
{
printf("%s\n", __FILE__); //进行编译的源文件
printf("%d\n", __LINE__); //文件当前的行号
printf("%s\n", __DATE__); //文件被编译的日期
printf("%s\n", __TIME__); //文件被编译的时间
printf("%d\n", __STDC__); //如果编译器遵循ANSI C,其值为1,否则未定义
return 0;
}
(2)#define
1 #define 定义标识符
#define 定义标识符会在预编译阶段完成替换
例子:
#define MAX 100;
#define STR "bitejiuyeke"
#define do_forever for(;;)
问题:在define定义标识符的时候,要不要在最后加上? 建议不要加上; ,这样容易导致问题。
如:
#define MAX 100;
int main()
{
int m = 0;
if (3 > 5)
m = MAX;
else
m = -1;
return 0;
}
m=MAX;这里会出现语法错误
2 #define 定义宏
规则:
例子:
#define MAX(x,y) (x>y?x:y)
int main()
{
int a = 10;
int b = 20;
int m = MAX(a, b);
printf("%d\n", m);
return 0;
}
一个小坑:
一个相乘函数原本想算出结果30,实际计算的结果是11,没有达到预期效果
#define SQUARE(x) x*x
int main()
{
int ret = SQUARE(5 + 1); //int ret = 5+1*5+1;
return 0;
}
解决: 写成这样#define SQUARE(x) ((x)*(x))
所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中
的操作符或邻近操作符之间不可预料的相互作用。
3 #define 替换规则
4 #和##
#把一个宏参数变成对应的字符串。
一个例子:
修改:
##可以把位于它两边的符号合成一个符号。它允许宏定义从分离的文本片段创建标识符。
例子:
5 带副作用的宏参数
例子:
想要求出3和4谁大,却求出了别的结果
6 宏和函数对比
宏有时候可以做函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到
如:
#define MALLOC(num,type) (type*)malloc(num*sizeof(type))
int main()
{
int* p = (int*)malloc(10 *sizeof(int));
MALLOC(10, int);
return 0;
}
命名约定
但是有例外: offsetof getchar 都是宏
(3) #undef
(4)命令行定义
这里不过多介绍
(5) 条件编译
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件
编译指令。
比如说:
调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译。
例子:
如果定义了__DEBUG__,就打印出arr数组元素中的每个值,否则不打印
#define __DEBUG__
int main()
{
int i = 0;
int arr[10] = { 0 };
for (i = 0; i < 10; i++)
{
arr[i] = i;
#ifdef __DEBUG__
printf("%d\n", arr[i]); //为了观察数组是否赋值成功。
#endif
}
return 0;
}
//#define __DEBUG__
int main()
{
int i = 0;
int arr[10] = { 0 };
for (i = 0; i < 10; i++)
{
arr[i] = i;
#ifdef __DEBUG__
printf("%d\n", arr[i]); //为了观察数组是否赋值成功。
#endif
}
return 0;
}
常见的条件编译指令:
例子:
即使#if #elif条件都满足也只会进入#if语句打印hehe, 用法类似于if-else if-else语句,只能进入一条语句中
注意:
都是判断是否有定义,前两个功能相同,后两个功能相同
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
(6) 头文件的包含