c中对宏的理解(面试题)

1、gcc的编译过程:预处理、编译、汇编、链接

c中对宏的理解(面试题)_第1张图片

预处理:宏替换、删除注释、头文件包含、条件编译 -E (不会发生报错)生成预编译文件
将 01_code.c文件使用 gcc -E 01_code.c -o 01.i生成预编译文件01.i
在这里插入图片描述
可以10行的源文件看见生成800多行的预编译文件
c中对宏的理解(面试题)_第2张图片

编译:将预编译之后的文件编译成汇编文件
在这里插入图片描述
c中对宏的理解(面试题)_第3张图片

汇编:将汇编文件生成二进制文件
在这里插入图片描述

在这里插入图片描述

链接:将工程中的各个二进制文件+库函数+启动文件 生成可执行文件

edu@edu:~/work/c/day_03$ gcc 01.o -o 01

c中对宏的理解(面试题)_第4张图片

或者可以直接由预编译文件一步到位

在这里插入图片描述

2、宏的了解

使用关键字define叫做宏

#define MA 3                          //宏的定义形式(宏定义)

在预处理的时候使用 3 代替所有MA出现的位置(宏展开)
注意:不要再宏后面加 分号

#define MA 3;                          //错误的宏定义形式
if (MA>2){   //这个表达式中 就会出现 3; > 2这样的情况,所以加分号这种宏定义形式是错误的
   printf("大于\n");
}

3、不带参数的宏

宏的定义范围:是从定义处开始当前文件结束 都有效
#under可以结束宏的作用域(但是这个是不常用的)
宏没有归属,只有在当前文件中有效


#include 
void test06()
{
#define N 100 // 宏没有归属感,只要不影响使用,可以定义在当前文件中的任何位置中,所以这种定义方式也是正确的
    printf("N = %d\n", N);
}
int main(int argc, char const *argv[])
{
    test06();
    return 0;
}

在这里插入图片描述

4、带参数的宏

#include 
#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进行预编译在这里插入图片描述
c中对宏的理解(面试题)_第5张图片

注意事项:

(1)、宏定义的时候不能有数据类型
c中对宏的理解(面试题)_第6张图片
(2)、宏定义不能保证参数的完整性

#include 
#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();
}

c中对宏的理解(面试题)_第7张图片
(3)、宏不能作为 类、结构体的成员

5、带参宏(宏函数)和带参函数的区别

(1)、带参宏调用多少次就会展开多少次,执行代码的时候没有函数的调用过程,不需要压栈和弹栈,所以带参宏是浪费了栈的空间,因为在预编译的时候被多次展开,所以节省时间(典型的使用空间换取时间)
(2)、带参数的函数,只有一段,存储在代码段,所以在编译阶段会进行多次压入栈中、退出栈中,所以节省了空间,但是压栈和退栈浪费了时间(典型的使用时间换取空间)
(3)、带参数的函数中的参数有数据类型定义,但是带参宏中没有数据类型的定义

你可能感兴趣的:(嵌入式开发学习,c语言,开发语言)