C语言中宏的一些高级用法举例

简介: C语言中宏的一些高级用法举例

1.字符串化

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/time.h>
#define trance(x,format) \
        printf(#x " =% " #format "\n",x)
int main() {
    char flag = '1';
    trance(flag,c);
    trance(flag,d);
    char* name = "zifuchuanhua";
    trance(name,s);
    trance(name,x);
     return 0;
}

结果

2.标记的拼接

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/time.h>
#define trance(x,format) \
        printf(#x " =% " #format "\n",x)
#define trance2(i) trance(salary ## i,d)
int main() {
    int salary1 = 10000,salary2=1209,salary3=12345;
    trance2(1);
    trance2(2);
    trance2(3);
     return 0;
}

结果

3.宏的嵌套

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/time.h>
#define F(f) f(args)
#define args a,b
void test(int number1,int number2){
    printf("%d + %d = %d\n",number1,number2,number1 + number2);
} 
int main() {
    int a=5;
    int b=9;
    F(test);
     return 0;
}

结果

替换多条语句

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/time.h>
#define STUDY( name) do{    \
    printf("study %s\n",name); \
    printf("work %s\n",name); \
    printf("learn %s\n",name); \
    printf("diy %s\n",name); \
}while(0);
int main() {
char name[] = "qrs";
    STUDY(name);
     return 0;
}

防止头文件被重复包含

#ifndef xxxxx
#define xxxxx
#endif

宏的可变参数应用

方式1

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/time.h>
#define DEBUG(fmt, ...) \
             printf("[FILE: %s] [FUNCTION: %s] [LINE: %d] " fmt "\n", \
                    __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
int main() {
         int x = 100;
         int y = 200;
         DEBUG("x = %d, y = %d", x, y);
         return 0;
}

方式2

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/time.h>
#define DEBUG_ON 1
#if DEBUG_ON
#define DEBUG(fmt, ...) \
             printf("[FILE: %s] [FUNCTION: %s] [LINE: %d] " fmt "\n", \
                    __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define DEBUG(fmt,...)
#endif
int main() {
         int x = 100;
         int y = 200;
         DEBUG("x = %d, y = %d", x, y);
         return 0;
}

DEBUG_ON = 1

DEBUG_ON =0

方式3

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/time.h>
//使用这一行或注释这一行
#define DEBUG_ON 
#if DEBUG_ON
#define DEBUG(fmt, ...) \
             printf("[FILE: %s] [FUNCTION: %s] [LINE: %d] " fmt "\n", \
                    __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
#define DEBUG(fmt,...)
#endif
int main() {
         int x = 100;
         int y = 200;
         DEBUG("x = %d, y = %d", x, y);
         return 0;
}

4.常用宏


#define MEM_B(x) (*((byte *)(x)))
  • 一个field在结构体(struct)中的偏移量
#define FPOS(type,field)  ((dword) & ((type *)0)->field)
  • 得到一个结构体中field所占用的字节数
#define FSIZ(type,field) sizeof(((type*)O)->field)
  • 得到一个字的高位和低位字节
#define WORD_LO(xxx) ((byte)((word)(xxx) & 255))
#define WORD_HI(xxx) ((byte)((word)(xxx) >> 8))
  • 将一个字母转换为大写
#define UPCASE(c) (((c) >= 'a' && (c) <= 'z') ? ((c) - 0X20):(c))
  • 返回数组元素的个数
#define ARR_SlZE(a) (sizeof((a))/sizeof((a[0])))
  • container_of
    此宏在内核代码 kernel/include/linux/kernel.h 中定义
#define container_of(ptr, type, member) \
    (type *)((char *)(ptr) - (char *) &((type *)0)->member)

ptr 是指向结构体成员的指针;

type 是结构体类型名;

member 是结构体成员名。

宏定义包含一个单独的表达式,它执行以下操作:

  1. (type *)0:首先将整数 0 强制转换为指向 type 类型的指针,即创建一个空的 type 类型的指针,这样就能够在后续计算中使用结构体成员的偏移量。
  2. &((type *)0)->member:使用成员运算符 -> 访问结构体指针的成员 member,然后取其地址 &,即得到 member 在结构体中的偏移量。
  3. (char *):将偏移量强制转换为指向 char 类型的指针,以便在后续计算中以字节为单位进行偏移。
  4. (char *)(ptr):将传入的结构体成员指针 ptr 强制转换为指向 char 类型的指针,以便在后续计算中以字节为单位进行偏移。
  5. (char *)(ptr) - (char *) &((type *)0)->member:计算从结构体成员指针 ptr 到结构体指针的偏移量,即结构体成员 member 在结构体中的偏移量,然后用结构体指针的地址减去该偏移量,得到整个结构体的指针。
  6. (type *)((char *)(ptr) - (char *) &((type *)0)->member):将计算出的指针强制转换为指向 type 类型的指针,即返回整个结构体的指针。
    因此,这个宏定义的作用是通过一个结构体成员的指针,返回整个结构体的指针。它的实现原理是利用了 C 语言中结构体的内存布局特点,即结构体的第一个成员的地址就是结构体本身的地址,后续成员的地址依次递增。这样,通过结构体成员的偏移量,就能计算出整个结构体的地址。
#define container_of(ptr, type, member) ({            \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})

功能:根据结构体变量成员施址获取整个结构体的存储空间首地址

参数:

ptr:结构体变量的成员地址

type:结构体类型

member:结构体成员

  • #define 是 C 语言中的宏定义关键字,它定义了一个名为 container_of 的宏,宏参数有三个,分别是 ptr、type 和 member。
  • {} 是 C 语言中的代码块,宏定义的代码块中包含了两个语句。
  • typeof 是 GCC 编译器的一个扩展,它可以获取一个表达式的类型。
  • ((type *)0)->member 是一个结构体成员访问表达式,它的意思是访问 type 结构体中的 member 成员,并返回该成员的类型。
    __mptr 是一个指向 member 成员的指针,它指向的类型是 const typeof(((type *)0)->member) *,也就是 type 结构体中 member 成员的类型的常量指针。
  • (ptr) 是一个宏参数,它表示传入的结构体成员指针。
  • (char *)__mptr 将指向 member 成员的指针转换为 char 类型的指针,这样可以通过指针运算来计算整个结构体的指针。
  • offsetof 是 C 语言标准库中的一个宏,它可以计算一个结构体中某个成员相对于结构体起始地址的偏移量。
  • (type *)((char *)__mptr - offsetof(type, member)) 是整个宏的返回值,它的意思是从 member 成员的指针计算出整个结构体的地址,并将其转换为 type * 类型的指针。
    综上所述,这个宏定义实现了一个通用的容器类型转换技巧,可以在任何包含了指定成员的结构体中使用
    而 offsetof 定义在 kernel/include/linux/stddef.h ,如下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

举个例子,来简单分析一下 container_of 内部实现机制。

例如:

struct test{
    int a;
    short char b;c;
};
struct test *p = (struct test *)malloc(sizeof(struct test));
test_function(&(p->b));
int test_function(short *addr_b){//获取 struct test 结构体空间的首地址
    struct addr = test *addr;
    container_of(addr_b,struct test,b);
}

展开 container_of 宏,探究内部的实现:

typeof ( ( (struct test *)0 )->b ) ; (1)
typeof ( ( (struct test *)0 )->b ) *__mptr = addr_b ; (2)
(struct test *)( (char *)__mptr - offsetof(struct test,b)) (3)

(1) 获取成员变量 b 的类型 ,这里获取的就是 short 类型。这是 GNU_C 的扩展语法。

(2) 用获取的变量类型,定义了一个指针变量 __mptr ,并且将成员变量 b 的首地址赋值给它

(3) 这里的 offsetof(struct test,b)是用来计算成员 b 在这个 struct test结构体的偏移。 __mptr是成员 b 的首地址, 现在 减去成员 b 在结构体里面的偏移值,算出来的是不是这个结构体的首地址呀 。

宏和函数的区别

(1)函数调用时,先求出实参表达式的值,然后带入形参带参数的宏只进行简单的字符替换;

(2)函数调用是在程序运行时处理,分配临时内存;而宏展开(函函数),是在编译时进行的,展开时是不分配内存,也没有返回值,也没有值传递

(3)宏的参数没有类型,只是一个符号《展开时带入到指定字符串中。

(4)使用宏次数多时,宏展开后源程序变长,函数调用不会使源程序变长;

(5)宏替换只占用编译时间,不占用运行时间而函数调用占用的是运行时间(分配内存,传递参数,执行函数体)。

目录
相关文章
|
6月前
|
存储 程序员 C语言
c语言的if语句举例
c语言的if语句举例
98 0
|
6月前
|
存储 C语言
C语言数据的输入举例
C语言数据的输入举例
56 1
|
6月前
|
存储 C语言
C语言数据的输出举例
C语言数据的输出举例
54 1
|
2月前
|
C语言
C语言判断逻辑的高阶用法
在C语言中,高级的判断逻辑技巧能显著提升代码的可读性、灵活性和效率。本文介绍了六种常见方法:1) 函数指针,如回调机制;2) 逻辑运算符组合,实现复杂条件判断;3) 宏定义简化逻辑;4) 结构体与联合体组织复杂数据;5) 递归与分治法处理树形结构;6) 状态机管理状态转换。通过这些方法,可以更高效地管理和实现复杂的逻辑判断,使代码更加清晰易懂。
229 88
|
6月前
|
存储 数据处理 C语言
C语言高级应用探讨与实例
C语言高级应用探讨与实例
55 1
|
6月前
|
存储 C语言
简单c语言程序举例
简单c语言程序举例
76 1
|
3月前
|
存储 缓存 编译器
【C语言篇】scanf和printf万字超详细介绍(基本加拓展用法)(下篇)
scanf处理⽤⼾输⼊的原理是,⽤⼾的输⼊先放⼊缓存,等到按下回⻋键后,按照占位符对缓存进⾏解读。 解读⽤⼾输⼊时,会从上⼀次解读遗留的第⼀个字符开始,直到读完缓存,或者遇到第⼀个不符合条件的字符为⽌。
162 2
|
3月前
|
存储 C语言
【C语言篇】scanf和printf万字超详细介绍(基本加拓展用法)(上篇)
printf 的作⽤是将参数⽂本输出到屏幕。它名字⾥⾯的 f 代表 format (格式化),表⽰可以定制输出⽂本的格式。
75 1
|
5月前
|
存储 C语言
c语言scanf函数用法
c语言scanf函数用法
|
5月前
|
存储 C语言
C语言学习记录——联合体(共用体、特点、用法、联合体大小计算)
C语言学习记录——联合体(共用体、特点、用法、联合体大小计算)
77 2