基础[C语言] C语言宏编程

193 阅读2分钟

我们一般在看内核源码的时候经常看到如下代码,这就是宏函数

#define c67x00_sie_config(config, n)  (((config)>>(4*(n)))&0x3)

#define cdc_ncm_comm_intf_is_mbim(x)  ((x)->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_MBIM && \
				       (x)->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE)

什么是宏?

根据维基百科定义,宏就是 Macro 是一种批处理的称谓。

在计算机里的宏就是一种抽象,他根据一系列预定义规则进行替换的文本模式,C语言的宏预处理器只能完成简单的文本搜索和替换,但是这依然能够提高程序的简捷性。而C语言中宏定义也有很多指令,我们这里只讨论#define指令编程。

示例代码

这里我们定义了一个宏函数 log,对printf进行了封装

#include <stdio.h>

#define log(x, format) printf(#x "=%" #format "\n", x)

int main(void)
{
  char *name = "xing xing xing";
  log(name, s);
  log(name, x);

  return 0;
}

使用 gcc -E -C预处理后的文件

我们可以看到在预处理阶段,宏被替换为我们封装的printf函数 并完成字符转换

# 5 "./main.c"
int main(void)
{
  char *name = "xing xing xing";
  printf("name" "=%" "s" "\n", name);
  printf("name" "=%" "x" "\n", name);

  return 0;
}

使用 gcc -s 查看生成的汇编代码

我们看宏定义已经被转化为了 字符常量

	.file	"main.c"
	.text
	.section	.rodata
.LC0:
	.string	"xing xing xing"
.LC1:
	.string	"name=%s\n"
.LC2:
	.string	"name=%x\n"
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$16, %rsp
	leaq	.LC0(%rip), %rax
	movq	%rax, -8(%rbp)
	movq	-8(%rbp), %rax
	movq	%rax, %rsi
	leaq	.LC1(%rip), %rax
	movq	%rax, %rdi
	movl	$0, %eax
	call	printf@PLT
	movq	-8(%rbp), %rax
	movq	%rax, %rsi
	leaq	.LC2(%rip), %rax
	movq	%rax, %rdi
	movl	$0, %eax
	call	printf@PLT
	movl	$0, %eax
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (GNU) 13.2.1 20230801"
	.section	.note.GNU-stack,"",@progbits


image.png

宏定义虽然看着简单,但是它的灵活性确实毋庸置疑的,用得好可以使程序可读性和编程效率大幅提升,同时可维护性也会拔高一个层次。

宏的作用域

我们定义了如下代码

#define A 10

int main(void)
{
  int a1 = A;
  int b1 = B;
#define B 20

  int b2 = B;
#undef A

  int a2 = A;

  if (1) {
#define C 30
    int c = C;
  }
  int c2 = C;

  return 0;
}

void test (){
  int c3 = C;
}

预处理后我们得到如下预处理代码

这说说明了宏的作用域是根据声明的先后顺序决定的,与方法体无关,只要是在方法体后面引用且定义没有被取消,都是能够被替换的。

int main(void)
{
  int a1 = 10;
  int b1 = B;


  int b2 = 20;


  int a2 = A;

  if (1) {

    int c = 30;
  }
  int c2 = 30;

  return 0;
}

void test (){
  int c3 = 30;
}