利用上周和周末的时间, 学习了GCC相关的知识.
虽然在以前学习C的时候有接触过(gcc 的命令选项、 头文件、 宏定义、 头文件路径、 cpp(C语言预处理器)等知识 ),
但是在项目中还是发现遇到很多问题,
比如:
1、为什么宏定义使用do{...}while(0)
?
2、项目中的日志打印系统是怎么弄的?
3、C预处理器后的文件中的数字都是代表什么意思?
4、makefile文件中编译项目使用的GCC的选项有什么作用?
5、等等问题吧.
终于上周我在官网学习了这些知识,
do{...}while(0)
?定义宏的时候如果有多条语句; 需要{ }
把几条语句合起来…
不然替换到代码中可能由于括号等优先级等出现语法、逻辑错误.
#define device_init_wakeup(dev,val) \
{ \
device_can_wakeup(dev) = !!(val); \
device_set_wakeup_enable(dev,val);}
如果使用的细致, 替换后不出现问题也是可以的.
但是 类函数宏 调用的时候就应该像函数那样.
if(n > 0)
device_init_wakeup(d, v);
展开后为
if(n > 0)
{device_can_wakeup(dev) = !!(val);
device_set_wakeup_enable(dev,val);} ;
else
问题就出现在最后的那个 ;
,这样就会多一个空语句.
if 语句被;结束掉了,没法和else配对 (会出现语法错误)
规范语法书写!!
if(n > 0){
device_can_wakeup(dev) = !!(val);
device_set_wakeup_enable(dev,val);
}
;
else
{ }
就会出现语法错误.所以使用do {...} while(0)
可以解决这些问题
#define device_init_wakeup(dev,val) \
do { \
device_can_wakeup(dev) = !!(val); \
device_set_wakeup_enable(dev,val); \
} while(0)
如果宏展开后,
学习网址
https://gcc.gnu.org/onlinedocs/gcc-7.5.0/gcc/
比如 :
预处理、编译、汇编、链接.
file.c C语言源代码
file.i 预处理后的文件
file.h 头文件
file.s 汇编代码
C源代码、 预处理的C代码、 汇编文件、 目标文件、 可执行文件.
/ usr/ include / 头文件
/ usr/ lib / 库文件
/ usr / lib / gcc / 编译器本身的可执行文件、 库文件
/ usr / local / lib / gcc /
-c
编译、汇编源文件, 但是不链接, 最终输出是以每个文件的目标文件的形式.
.o
中间目标文件
-E
在与处理阶段后停止. 不需要正确运行编译器.
-S
再适当的编译阶段后停止, 不需要组装.
.s
汇编文件
-o file
生成指定的输出文件
将输出放到文件file中,
-g
以操作系统的本机格式生成调试信息, GDB可以使用此调试信息。
-Idir
指定额外的头文件搜索路径
将目录dir添加到要在预处理过程中搜索头文件的目录列表中。
使用-I、-isystem或-idirafter指定的目录来查找 #include“file” 和 #include 。
您可以在命令行上指定任何数目或这些选项的组合,以在多个目录中搜索头文件。
- 对于include指令的引用形式,首先搜索当前文件的目录。
- 对于include指令的引号形式,指定的目录为 -iquote 选项在命令行中按从左到右的顺序搜索。
- …
- 扫描标准系统目录。
-Ldir
指定额外的函数库搜索路径
将目录dir添加到要搜索的目录列表中
-l
。制定编译的时候,搜索库的路径。
-llibrary
连接时搜索指定的函数库
链接时搜索名为library的库。
gcc -lcurses hello.c
. 使用 ncurses 库编译程序
链接器在标准目录列表中搜索该库,该库实际上是一个名为 liblibrary.a。链接器然后使用该文件,就好像它是通过名称精确指定的一样。
搜索的目录包括几个标准系统目录以及您指定的任何目录 -L。
-pthread
与POSIX线程库链接。
-shared
生成共享目标文件。通常用在建立共享库时。
产生一个共享对象,然后可以将其与其他对象链接以形成可执行文件。
并非所有系统都支持此选项。
还必须指定用于编译的相同选项集(
-fPIC
)
-fPIC
如果目标机器支持,则发出与位置无关的代码,该代码适用于动态链接并避免对全局偏移表的大小进行任何限制。
这样的代码通过全局偏移表(GOT)访问所有常量地址。
官网网址
https://gcc.gnu.org/onlinedocs/gcc-7.5.0/cpp/
包括头文件、宏、预处理指令、选项、 预处理器的输出、
1、注释代码(就代码可能以后使用)
#if 0
代码
#endif
2、宏可变参数, 打印日志等函数时间信息
#define myprintf(format, ...) fprintf(stderr, format, ##__VA_ARGS__)
3、使用 宏串联 结构体数组赋值
#define COMMOND(NAME) {#NAME, NAME ## _commond}
struct commond {
char *name;
void *function(void);
};
struct commond commonds[] = {
{"quit", quit_commond},
{"help", help_commond},
COMMOND(quit),
COMMOND(help)
};
扩展为:
struct commond commonds[] = {
{"quit", quit_commond},
{"help", help_commond},
{"quit", quit_commond},
{"help", help_commond}
};
4、利用系统定义宏,打印相关信息
printf("__FILE__:", __FILE__); //"test1.c"
printf("__LINE__", __LINE__); //41
printf("__LINE__", __LINE__); //42
printf("__DATE__:", __DATE__); //"Apr 12 2020"
printf("__TIME__:", __TIME__); //"22:21:31"
printf("__COUNTER__ :", __COUNTER__); //0
printf("__COUNTER__ :", __COUNTER__); //1
printf("__VERSION__", __VERSION__); //"4.2.1 Compatible Apple LLVM 10.0.1 (clang-1001.0.46.4)"
5、打印出错信息
#error "睡觉吧, 干啥活呢, 睡觉不香么."
#ifdef __vax__
#error "Won't work on VAXen. See comments at get_last_object."
#endif
# linenum filename flags
它们的意思是起源于文件filename在linenum行
flag的含义;
文件名后面是零个或多个标记,即“1”、“2”、“3”或“4”。如果有多个标志,则用空格分隔它们。以下是含义:
1
2
3
4