c语言从0开始_14预处理

预处理指令

程序员所编写的代码并不能被真正的编译器编译,需要一段程序翻译一下
翻译的过程叫做预处理,被翻译的代码叫做预处理指令,以#开头的都是预处理指令

查看预处理的结果

    gcc -E code.c   把预处理的结果显示到屏幕上
    gcc -E code.c -o code.i 把预处理的结果存储到code.i文件中

预处理指令的分类

#include 文件包含

    #include <> 从系统指定的路径查找并导入头文件
    #include "" 先从当前路径查找,如果找不到再从系统指定路径查找并导入头文件
        操作系统是通过设置环境变量来指定头文件的系统查找路径,还可以通过编译参数 -I /path
        环境变量的设置通过 系统配置文件设置 : ~/.bashrc

#define 定义宏

宏常量
        #define MAX 100
            优点:提高代码的可扩展性(方便批量修改)、提高安全性(常量)、提高代码可读性、可以用在case后面
            注意:一般宏名全部大写,末尾不要加分号,会把MAX后的东西全部替换,所以不加
            【函数名:小写+下划线、全局变量:首字母大写、局部变量:全部小写、指针变量:+p、数组:arr、字符串str】
        预定义好的宏:
            __func__   获取函数名
            __FILE__   获取文件名
            __LINE__   获取当前行号
            __DATE__   获取当前日期
            __TIME__   获取当前时间
  _cplusplus  分辨C++文件编译器和C++编译器

    删除宏:
    #undef 宏名

    在编译时也可以定义宏:
    gcc -D DEBUG
宏函数

其实就是带参数的宏
不是真正的函数,不检查参数类型、没有传参、没有返回值,只有计算的结果
#define sum(a,b) a+b
1、把代码使用的宏函数替换为 宏函数后面的代码
2、把宏函数代码中使用的参数替换为调用者提供的参数
注意:宏函数、宏常量不能换行,但是可以使用续行符 \ 也可以使用大括号保护代码
优点:
1、不是真正的函数调用,没参数传递过程,也没返回值,所以运行速度比函数要快。
2、不限制参数类型,任何类型都可以使用,代码的通用性强。
缺点:
1、没有类型检查,安全性代低。
2、过多使用会造成代码冗余,导致代码段增大,浪费内存。
3、可能会产生二义性。
定义宏函数要注意的问题:
1、给每个参数加一个小括号,避免产生二义性。
2、使用小括号、大括号包含整个宏函数代码,进行保护。
3、宏函数的代码不换行,可以使用续行符 \。
4、宏函数的参数不要使用自变运算符。

宏的二义性

由于宏代码所处的位置、参数不同可能导致宏有不同的功能,这种叫做宏的二义性
如何避免:宏函数整体加小括号、每个参数都加小括号
注意:使用宏函数时不要提供带自变运算符的变量作为参数
注意:容易出选择题,例如:问哪个宏可能有二义性,问宏函数的结果
例子:#define sum(a,b) ((a)+(b))

    常见的笔试面试题:
        如果是普通类型,他们从功能上没有任意区别
    #define INTP int*
        INTP p1,p2,p3;  //只有p1是指针,p2p3都是int类型变量

    typedef int* INTP;
        INTP p1,p2,p3;  //p1p2p3都是指针变量

宏函数与普通函数的区别?
它们是什么
宏函数:不是真正的函数,只是代码替换,用起来像函数而已
函数: 是一段具有某项功能代码的集合,编译成二进制指令存储在代码段,有独立的命名空间、栈内存

有什么不一样:
函数: 返回值 类型检查 安全 入栈、出栈 速度慢 跳转
宏函数:运算结果 通用 危险 替换 速度快 冗余

条件编译

用于版本控制和编译器选择
根据条件决定了哪些代码是否参与最终的编译

    版本控制: [#if 0 注释代码]   
        #if, #ifdef, #ifndef, #else, #elif, #endif
    
/* */注释不能嵌套,但是#if 0可以嵌套注释

代码注释

    // 单行注释,使用麻烦,早期的编译器不支持
    /*不能嵌套使用*/ 不能嵌套使用
    #if 0|1
        适合注释大段代码
    #endif

判断、调试

-D宏名 在编译时添加宏
#ifdef 宏名
#else
#endif

判断代码的编译、运行的环境:
#ifdef _cplusplus
printf(“C++编译器”)
#else
printf(“C编译器”)
#endif

#if defined(APPLE)
puts(“我用的是mac os”);
#elif defined(WIN32) || defined(_WIN32) || defined(WIN32)
puts(“我用的是windows os”);
#elif defined(linux)
puts(“我用的是linux os”);
#endif

    代码调试:
    #ifdef DEBUG
        #define debug(...) printf(__VA_ARGS__)
    #else
        #define debug(...)
    #endif

     用于调试程序的宏函数:
#ifdef DEBUG
#define debug(fmt,...)\
{\
    printf("File:%s Line:%u Func:%s Info:",__FILE__,__LINE__,__func__);\
    printf("\033[01;34m");\
    printf(fmt,##__VA_ARGS__);\
    printf("\033[00m\n");\
}
#else
#define debug(fmt,...) if(0)
#endif

    #define error(...) printf("%s:%s:%d %s %m %s %s\n",__FILE__,__func__,__LINE__,__VA_ARGS__,__DATE__,__TIME__)
检测报错 %m可以打印报错        

其它预处理指令

#error “字符串” 产生一条件编译错误信息(不会生成可执行文件),与#if系列指令配合使用才有意义。

#warning “字符串” 产生一条件编译警告信息(正常生成可执行文件),要与#if系列指令配合使用才有意义。

#line line_number “filename” 设置代码的行号和文件名。

#pragma 可以让编译器执行某些事. 因为#pragma命令的执行很特殊,不同的编译器使用有所不同。

#pragma pack(n) 用于修改内存对齐、补齐最大字节数 n等于2的n次方才有意义,n<默认最大字节数。

#pragma GCC poison <标识符> 把某个标识符定义为毒药,禁止使用,一般用于禁用goto语句。

加上u就可以把常量变成无符号的基本类型
例:#define YEAR_SEC(360024365*100u)
此处不加u,那就超了,无法显示

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值