1、gcc的编译过程:预处理、编译、汇编、链接
预处理:宏替换、删除注释、头文件包含、条件编译 -E (不会发生报错)生成预编译文件
将 01_code.c文件使用 gcc -E 01_code.c -o 01.i
生成预编译文件01.i
可以10行的源文件看见生成800多行的预编译文件
编译:将预编译
之后的文件编译成汇编文件
汇编:将汇编文件
生成二进制文件
链接:将工程中的各个二进制文件+库函数+启动文件
生成可执行文件
edu@edu:~/work/c/day_03$ gcc 01.o -o 01
或者可以直接由预编译文件一步到位
2、宏的了解
使用关键字define叫做宏
#define MA 3 //宏的定义形式(宏定义)
在预处理的时候使用 3 代替所有MA出现的位置(宏展开)
注意:不要再宏后面加 分号
#define MA 3; //错误的宏定义形式
if (MA>2){ //这个表达式中 就会出现 3; > 2这样的情况,所以加分号这种宏定义形式是错误的
printf("大于\n");
}
3、不带参数的宏
宏的定义范围:是从定义处开始
到当前文件结束
都有效
#under可以结束宏的作用域(但是这个是不常用的)
宏没有归属,只有在当前文件中有效
#include <stdio.h>
void test06()
{
#define N 100 // 宏没有归属感,只要不影响使用,可以定义在当前文件中的任何位置中,所以这种定义方式也是正确的
printf("N = %d\n", N);
}
int main(int argc, char const *argv[])
{
test06();
return 0;
}
4、带参数的宏
#include <stdio.h>
#define MY(a, b) a *b
void test00()
{
printf("MY(a,b) = %d\n", MY(2, 3)); // 使用 10*20代替 MY(2, 3)
}
int main()
{
test00();
}
使用 gcc -E 01_code.c -o 01.i进行预编译
注意事项:
(1)、宏定义的时候不能有数据类型
(2)、宏定义不能保证参数的完整性
#include <stdio.h>
#define MY(a, b) a *b
#define MY_1(a, b) a *b
#define MY_2(a, b) ((a) * (b))
#define MY_3(a, b) (a) * (b)
void test00()
{
printf("MY(a,b) = %d\n", MY(2, 3)); // 使用 a*b代替 MY(2, 3)
printf("MY_1(a,b) = %d\n", MY_1(2 + 2, 3 + 3)); // 2 + 2 * 3 + 3
printf("MY_2(a,b) = %d\n", MY_2(2 + 2, 3 + 3)); //((2+2) * (3 + 3))
printf("MY_3(a,b) = %d\n", MY_3(2 + 2, 3 + 3)); //(2+2) * (3 + 3)
}
int main()
{
test00();
}
(3)、宏不能作为 类、结构体的成员
5、带参宏(宏函数)和带参函数的区别
(1)、带参宏调用多少次就会展开多少次,执行代码的时候没有函数的调用过程,不需要压栈和弹栈,所以带参宏是浪费了栈的空间,因为在预编译的时候被多次展开,所以节省时间(典型的使用空间换取时间)
(2)、带参数的函数,只有一段,存储在代码段,所以在编译阶段会进行多次压入栈中、退出栈中,所以节省了空间,但是压栈和退栈浪费了时间(典型的使用时间换取空间)
(3)、带参数的函数中的参数有数据类型定义,但是带参宏中没有数据类型的定义