昨天分享了c语言里面的共用体、大小端模式、枚举的用法,今天我们来继续分享c语言里面预处理用法。争取在年假期间给大家分享完c语言里面的一些基本用法。如果读者在看到文章里面没有分享到的知识点,您可以私发或者在公众号后台把要讲的知识可以发给我,后面我会罗列出来,最后写成推文分享出来。同时预计在年后假期结束后可能会开始写一系列从零到尾的c++推文(自己也是一边学,一边总结的,欢迎大家来一起学习和交流)。
一、预处理: 1、什么是预处理? 想必每个稍微写过一点c语言程序的都会写到如下面代码所示,这个就是表示预处理(主要是这个"#"符号):
#include <stdio.h>
2、理解一个我们自己写的一个程序到可执行程序的详细过程: (1)源码.c->(编译)->elf可执行程序 (2)源码.c->(编译)->目标文件.o->(链接)->elf可执行程序 (3)源码.c->(编译)->汇编文件.S->(汇编)->目标文件.o->(链接)->elf可执行程序 (4)源码.c->(预处理)->预处理过的.i源文件->(编译)->汇编文件.S->(汇编)->目标文件.o->(链接)->elf可执行程序 说明: 预处理用预处理器,编译用编译器,汇编用汇编器,链接用链接器,这几个工具再加上其他一些额外的会用到的可用工具,合起来叫编译工具链。gcc就是一个编译工具链。上面第四个是最为详细的编译过程。
3、gcc中只预处理不编译的方法:
4、C语言预处理代码实战: (1)、#include(#include <>和#include ""的区别),我们先来看下面的代码演示,我先在root@ubuntu-virtual-machine:/mnt/hgfs/day#下创建了两个文件:
root@ubuntu-virtual-machine:/mnt/hgfs/day# ls
hello.c test.h
root@ubuntu-virtual-machine:/mnt/hgfs/day#
然后我们在test.h里面定义的了两个数据类型int a 和float b,接着我在hello,c里面引用它,分别用"test.h"和<test.h>,然后你会发现自己写的这个头文件包含进去使用"test.h"可以行得通,但是使用<test.h>这种形式是行不通的:
int a;
float b;
#include <stdio.h>
#include <test.h>
int main(void)
{
a=54;
printf("the a is %d\n",a);
return 0;
}
编译结果:
root@ubuntu-virtual-machine:/mnt/hgfs/day# gcc hello.c
hello.c:2:10: fatal error: test.h: 没有那个文件或目录
#include <test.h>
^~~~~~~~
compilation terminated.
上面实验现象分析:
(2)、注释:
下面是实现过程现象:
#include <stdio.h>
int main(void)
{
// 强制类型转换
int a;
char b;
a = 1;
b = (char)a;
printf("b = %d.\n", b); // b=1
return 0;
}
编译过程:
root@ubuntu-virtual-machine:/mnt/hgfs/day# gcc -E hello.c -o hello.i
root@ubuntu-virtual-machine:/mnt/hgfs/day# ls
'\' a.out hello.c hello.i test.h
root@ubuntu-virtual-machine:/mnt/hgfs/day#
root@ubuntu-virtual-machine:/mnt/hgfs/day# cat hello.i
然后我们可以看到这里面发生的现象了:
extern void funlockfile (FILE *__stream) __attribute__
((__nothrow__ , __leaf__));
# 868 "/usr/include/stdio.h" 3 4
# 2 "hello.c" 2
# 4 "hello.c"
int main(void)
{
int a;
char b;
a = 1;
b = (char)a;
printf("b = %d.\n", b);
return 0;
}
通过上面验证,读者可以看到这些注释全没了。
(3)、条件编译:
#include "stdio.h"
#define NUM
int main(void)
{
int a = 0;
#ifdef NUM // 如果前面有定义NUM这个符号,成立
a = 111;
printf("#ifdef NUM.\n");
#else // 如果前面没有定义NUM这个符号,则执行下面的语句
a = 222;
printf("#elif.\n");
#endif
printf("a = %d.\n", a);
return 0;
}
输出结果:
root@ubuntu-virtual-machine:/mnt/hgfs/day# gcc hello1.c
root@ubuntu-virtual-machine:/mnt/hgfs/day# ./a.out
#ifdef NUM.
a = 111.
我们也来看一下这个过程中到底发生了什么:
root@ubuntu-virtual-machine:/mnt/hgfs/day# gcc -E hello1.c -o hello1.i
root@ubuntu-virtual-machine:/mnt/hgfs/day# ls
'\' a.out hello1.c hello1.i hello.c hello.i
test.h
编译过程:
# 868 "/usr/include/stdio.h" 3 4
# 2 "hello1.c" 2
# 6 "hello1.c"
int main(void)
{
int a = 0;
a = 111;
printf("#ifdef NUM.\n");
printf("a = %d.\n", a);
return 0;
}
这个代码演示是if形式:
#include <stdio.h>
#define NUM 1
int main(void)
{
int a = 0;
#if (NUM == 0) // 如果前面有定义NUM这个符号,成立
a = 111;
printf("#ifdef NUM.\n");
#else // 如果前面没有定义NUM这个符号,则执行下面的语句
a = 222;
printf("#elif.\n");
#endif
return 0;
}
输出结果:
root@ubuntu-virtual-machine:/mnt/hgfs/day# gcc hello2.c
root@ubuntu-virtual-machine:/mnt/hgfs/day# ./a.out
#elif.
二、总结: 好了今天的分享就到这里了,这里我分享的重点是要知道这个预处理到底发生了什么,这是自己以前没弄明白的东西,虽然有些书上也写的比较明白,但是实际具体细节,自己还真不明白,通过这次的总结学习,是彻底弄明白了这里面的发生了什么。其实在stm32里面,我们经常都会跟这个预处理打交道,特别是多个文件重复被包含的问题(这里面有点跟宏定义一样,宏定义明天会分享的):