GCC、CPP学习篇

GCC 学习篇

利用上周和周末的时间, 学习了GCC相关的知识.

虽然在以前学习C的时候有接触过(gcc 的命令选项、 头文件、 宏定义、 头文件路径、 cpp(C语言预处理器)等知识 ),

但是在项目中还是发现遇到很多问题,

比如:
1、为什么宏定义使用do{...}while(0) ?
2、项目中的日志打印系统是怎么弄的?
3、C预处理器后的文件中的数字都是代表什么意思?
4、makefile文件中编译项目使用的GCC的选项有什么作用?
5、等等问题吧.

终于上周我在官网学习了这些知识,

1、为什么宏定义使用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)

如果宏展开后,

2、GCC的选项?

学习网址

https://gcc.gnu.org/onlinedocs/gcc-7.5.0/gcc/

比如 :

C源码编译的中间过程文件

预处理、编译、汇编、链接.

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)访问所有常量地址。

3、C预处理器

官网网址

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

4、C预处理器后文件中数字代表的意思?

GCC、CPP学习篇_第1张图片

# linenum filename flags

它们的意思是起源于文件filename在linenum行

flag的含义;

文件名后面是零个或多个标记,即“1”、“2”、“3”或“4”。如果有多个标志,则用空格分隔它们。以下是含义:

1

  • 这表示一个新文件的开始。

2

  • 这表示返回到一个文件(在包含另一个文件之后)。

3

  • 这表明以下文本来自系统头文件,因此应禁止某些警告。

4

  • 这表明应该将以下文本作为隐式extern“C”块进行包装。

欢迎关注公众号:
GCC、CPP学习篇_第2张图片

你可能感兴趣的:(C语言,c语言)