宏函数

宏替换:

定义宏名:

    #define 宏名 会被替换的内容

使用宏名:

printf("%d\n",宏名);
注意:在预处理阶段,预处理器会把代码使用的宏名替换成宏名后面的那段内容。

宏常量:

#define 宏名 字面值数据
给没有意义的字面值数据,取一个有意义名字,代替它,这样可以提高代码的可读性、可扩展性,还可以方便代码扩展。

宏表达式:

#define 宏名 表达式、操作、更复杂的标识符 
#define STU_FORMAT "%s %c %hd %d"
#define sf scanf
#define YEAR_SEC (3600*24*365ul)

预定义的宏:

编译器预定义的宏:

    __FILE__ 获取当前文件名
    __func__ 获取当前函数名
    __LINE__ 获取当前行号
    __DATE__ 获取当前日期
    __TIME__ 获取当前时间
    __WORDSIZE 获取当前编译器的位数
    适合用来显示警告、错误信息。

标准库预定义的宏:

    limits.h 头文件中定义的所有整数类型最大值、最小值
    #define SCHAR_MIN (-128)
    #define SCHAR_MAX 127 
    #define UCHAR_MAX 255

    #define SHRT_MIN  (-32768)
    #define SHRT_MAX  32767
    #define USHRT_MAX 65535

    #define INT_MIN   (-INT_MAX - 1)
    #define INT_MAX   2147483647
    #define UINT_MAX  4294967295U

    #define LLONG_MAX    9223372036854775807LL
    #define LLONG_MIN    (-LLONG_MAX - 1LL)
    #define ULLONG_MAX   18446744073709551615ULL

    stdlib.h 头文件定义两个结标志
    #define EXIT_SUCCESS (0)
    #define EXIT_FAILURE (-1)

宏函数:

#define 宏名(a,b,c,...) a+b*c

宏函数不是真正的函数,而是带参数的宏,只是使用方法像函数而已。
在代码中使用宏函数,预处理时会经历两次替换,第一次把宏函数替换成它后面的一串代码、表达式,第二次把宏函数中的参数替换到表达式中。
如果宏函数后面的代码有多行,可以使用大括号包括,进行保护。

       #define 宏名(a,b,c,...) {代码1; 代码2; ...}

宏函数后面的代码不能直接换行,如果代码确定太长,可以使用续行符换行。

#define 宏名(a,b,c,...) {   \
  代码1; \
  代码2; \
  ...    \
}

注意:以上写法使用者可以不加分号,统一语法的一致性,强制使用者加分在宏函数末尾加分号,可以写成以下格式

#define 宏名(a,b,c,...) do{   \
    代码1; \
    代码2; \
    ...    \
}while(0)

普通函数与宏函数的优缺点?

宏函数的优点:

1、执行速度快,它不是真正的函数调用,而是代码替换,不会经历传参、跳转、
返回值。
2、不会检查参数的类型,因此通用性强。
宏函数的缺点:
1、由于它不是真正的函数调用,而是代码替换,每使用一次,就会替换出一份代码,会造成代码冗余、编译速度慢、可执行文件变大。
2、没有返回值,最多可以有个执行结果。
3、类型检查不严格,安全性低。
4、无法进行递归调用。

函数的优点:

1、不存在代码冗余的情况,函数的代码只会在代码段中存储一份,使用时跳转过去执行,执行结束后再返回,还可以附加返回值。
2、安全性高,会对参数进行类型检查。
3、可以进行递归调用,实现分治算法。

函数的缺点:

1、相比宏函数它的执行速度慢,调用时会经历传参、跳转、返回等过程,该过程耗费大量的时间。
2、类型专用,形参什么类型,实参必须是什么类型,无法通用。

什么样的代码适合封装成宏函数?

1、代码量少,即使多次使用也不会造成代码段过度冗余。
2、调用次数,但执行次数多。
3、对返回值没有要求。

设计宏时要注意的问题:

1、末尾不要加分号。
2、多加小括号防止产生二义性。
3、不要使用自加、自减的变量给宏函数提供参数。

练习3:封装一个malloc、free函数

    my_malloc 它要记录my_malloc的调用位置,申请到的内存地址
    my_free 它要记录my_free的调用位置,释放到的内存地址

void* _my_malloc(size_t size,const char* file,const char* func,size_t line)
{
    void* ptr = malloc(size);
    printf("%s %s %d 申请了%d字节的内存,地址是%p\n",file,func,line,size,ptr);
    return ptr;
}
// 由于my_malloc必须有返回值,所以只能使用这种方式中转一下
#define my_malloc(size) _my_malloc(size,__FILE__,__func__,__LINE__)

#define my_free(ptr) {\
    free(ptr);\
    printf("%s %s %d 释放了内存 %p\n",__FILE__,__func__,__LINE__,ptr);\
}

条件编译:

条件语句(if、switch、for、while、do while)会根据条件选择执行哪些代码,预处理器根据条件选择哪些代码参与下一步的编译。

负责条件编译的预处理指令有:

    #if/#ifdef/#ifndef/#else if#else/#endif

头文件卫士:

#ifndef FILE_H // 判断FILE_H宏是否正在,不存在则条件为真
#define FILE_H // 定义FILE_H宏

#endif//FILE_H // #ifndef的结尾

这种固定写法,一般在头文件中使用,它能防止头文件被重复包含。

注释代码:

// 只能注释单行代码,早期的编译器不支持该用
/* 多行注释,但不能嵌套 */
#if 0|1
可注释大块代码,可以嵌套
#endif 

版本、环境判断:

编译器的位数:
    #if __WORDSIZE == 64
    #endif 
操作系统:
    #if __linux__
    #endif 

    #if __WIN32 | __WIN32__ | __WIN64__
    #endif 
判断gcc还是g++:
    #if __cplusplus

    #else 

    #endif 

不常用的预处理指令:

#line <常整数> 设置代码的行号,目前没有发现它有什么用
#error "在预处理阶段提示错误信息",一旦预处理遇到它,将不再继续编译,它不能单独使用必须与条件判断系列语句配合使用 
#warning "在预处理阶段提示警告信息" 不能建议单独使用,最好与条件判断系列语句配合使用。
#pragma GCC poison <标识符> 把标识符设置病毒,禁止在代码中使用

每个系统在进行对齐和补齐都有一个最大对齐和补齐字节数n,也就是超出n字节按n字节计算,例如:linux32系统n=4,windows32 n=8

#pragma pack(n)  设置最大对齐和补齐字节数
设置要求:
    1、n < 系统默认的最大对齐、补齐字节数
    2、n必须是2的x次方,也就是必须是1、2、4、8、16这一类的整数

宏函数的变长参数:

#define func(...) __VA_ARGS__
注意:这种用法必须配合,printf/fprintf/sprintf系列支持变长参数的函数使用。

在编译时定义宏:

gcc xxx.c -D ARR_LEN=3
-D ARR_LEN=3 <=> #define ARR_LEN 3
DEUBG宏:

专门用于调试程序的宏函数,这种宏函数在程序测试、调试、试运行阶段执行,在程序正式上线阶段不执行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 在C语言和C++语言中,一个标识符被允许表示一个称为宏的字符串,它可以是常量、表达式、格式字符串等,程序中出现的所...
    CrystalZhu阅读 2,394评论 0 0
  • 宏函数知识点: 宏函数是一种只进行简单替换的函数,在预处理时进行函数的替换。 编译分为四步:预处理,编译,汇编,链...
    Jinna_8af2阅读 3,952评论 0 1
  • 宏最大的坏处就是不判断输入类型,所以编译时容易出错 #pragma once与#ifndef #define .....
    木鱼_cc阅读 3,262评论 0 0
  • 通过 gdb 查看程序的汇编代码,比较宏和宏函数的工作效率。 程序 例子中的最大值实现,宏和函数逻辑基本相同。宏在...
    wenfh2020阅读 1,823评论 0 0
  • 1.宏 概述 宏可以定义一个宏常量,或者定义一个宏函数。其实本质都是字符替换。代码预编译的时候会展开,所以它的运行...
    向日葵彩虹阅读 1,585评论 0 0

友情链接更多精彩内容