嵌入式C必看(GCC/预处理)

嵌入式C必看(GCC/预处理)

1 GCC

  • GCC概述

    GCC最初的全名是GNU C Compiler随着GCC支持的语言越来越多,它的名称变成了GNU Compiler Collection翻译官/翻译组织

  • C语言编译过程

    一个 C/C++文件要经过预处理(preprocessing)、编译(compilation)、汇编(assembly)和链接(linking)等 4 步才能变成可执行文件

  • GCC编译选项

2 预处理

  • 预处理概述

    预处理指令是一些特殊的命令,用于在编译过程中进行文本替换、条件编译、宏展开等操作,从而对源代码进行预处理。预处理指令以 # 符号开头,并且在其后可以包含关键字、标识符、参数和参数值,形成一种类似于宏的语法。

    预处理指令是在编译过程中第一个被处理的部分,它会在实际编译之前对源代码进行处理。常见的预处理指令包括 #include#define#ifdef#ifndef#if#else#endif 等。

  • 常用预处理
    1. #include#include 是用于将一个文件的内容包含到当前文件中的预处理指令。它通常用于包含头文件,以便在当前文件中可以使用头文件中定义的函数、变量或宏。
    2. #define#define 用于定义一个宏(Macro),它是一种用于在源代码中进行文本替换的方式。可以使用 #define 定义常量、函数或代码块的缩写,从而在源代码中进行简单的替换操作。
    3. #ifdef#ifdef 是条件编译的一种形式,用于检查一个宏是否已经定义,如果已定义,则编译后面的代码块,否则跳过。
    4. #ifndef#ifndef#ifdef 相反,它用于检查一个宏是否未定义,如果未定义,则编译后面的代码块,否则跳过。
    5. #if#if 是条件编译的一种通用形式,可以通过在后面跟随表达式进行条件判断,如果表达式为真,则编译后面的代码块,否则跳过。
    6. #else#else 是在条件编译中的备选分支,它与 #if#ifdef#ifndef 配套使用,用于在条件不满足时执行备选代码块。
    7. #endif#endif 用于关闭一个条件编译块,与 #if#ifdef#ifndef 配套使用,表示条件编译块的结束。
    //括号的区别
    #define ABC 5+3
    printf("the %din",ABC*5)==5+3*5
        
    #define ABC (5+3)
    printf("the %din",ABC*5)==(5+3)*5
    
  • 预定义宏

    预定义宏(Predefined Macros)是在C和C++编译器中预先定义好的一些宏,可以在源代码中直接使用,用于获取一些与编译环境、操作系统、编译器版本等相关的信息。这些预定义宏在编译期间会被编译器替换成对应的值,从而在编译时获取到一些编译环境的信息。

    以下是一些常见的预定义宏及其用途:

    1. __FILE__:表示当前源文件的文件名(包括路径)。

    2. __LINE__:表示当前源文件中的行号。

    3. __DATE__:表示当前编译的日期,格式为字符串 “MMM DD YYYY”。

    4. __TIME__:表示当前编译的时间,格式为字符串 “HH:MM:SS”。

    5. __STDC__:表示当前编译器是否符合C语言标准的宏,如果符合,则值为1,否则未定义。

    6. __cplusplus:表示当前编译器是否符合C++语言标准的宏,如果符合,则值为一个整数,表示C++的版本号。

    7. __func__:表示当前函数的名称,C++中可以使用 __PRETTY_FUNCTION__ 宏来获取更详细的函数签名信息。

    这些预定义宏可以在源代码中直接使用,例如可以在代码中输出 __FILE____LINE__ 的值来进行调试信息输出,或者根据 __cplusplus 的值来进行条件编译,以适配不同的C++语言标准。需要注意的是,预定义宏的命名规则通常以双下划线开头和结尾,以避免与用户定义的宏冲突。

  • 宏定义下的# ##

    # 操作符:在宏定义中,# 操作符用于将宏参数转换为字符串常量。它可以在宏定义中的参数前面使用,并将参数的值转换为字符串,从而形成一个字符串常量

    //当这个宏被调用时,#x 将会把传入的参数 x 转换为一个字符串字面量。例如,STR(hello) 将会展开为 "hello"。
    
    #define STR(x) #x
    
    int main() {
        int num = 42;
        const char* str = STR(num);
        printf("%s\n", str); // 输出 "num"
        return 0;
    }
    

    ## 操作符:## 操作符用于将两个宏参数合并成一个单独的标识符。它可以在宏定义中的参数之间使用,并将其合并为一个标识符,从而形成一个新的标识符

    //当这个宏被调用时,x##y 将会把传入的参数 x 和 y 进行连接。例如,CONCAT(x, y) 将会展开为 ab。
    
    #define CONCAT(x, y) x ## y
    
    int main() {
        int num1 = 42;
        int num2 = 99;
        int num3 = CONCAT(num, 1); // 相当于 num1
        int num4 = CONCAT(num, 2); // 相当于 num2
        printf("%d, %d\n", num3, num4); // 输出 "42, 99"
        return 0;
    }
    

    ### 运算符在宏展开时的使用有一些限制和规则:

    1. # 运算符只能用于将宏参数转换为字符串字面量,不能用于连接标识符。
    2. ## 运算符只能用于连接标识符,不能用于将宏参数转换为字符串字面量。
    3. ### 运算符可以在同一个宏定义中多次使用,但它们之间不能有空格。
    4. ### 运算符在宏展开时的操作是在编译期进行的,而不是在运行时。
    5. ### 运算符在宏展开时的行为可能因编译器和语言标准而异,因此在使用时需要注意其兼容性。

    使用 ### 运算符可以在宏展开时进行字符串操作和标识符操作,从而实现更灵活和复杂的宏定义和使用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恐高宇航员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值