Posted on 

C语言 define

宏字符串拼接

标记粘贴运算符(##)

宏定义内的标记粘贴运算符(##)会合并两个参数。它允许在宏定义中两个独立的标记被合并为一个标记。例如:

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

#define tokenpaster(n) printf ("token" #n " = %d", token##n)

int main(void)
{
int token34 = 40;

tokenpaster(34);
return 0;
}

结果

1
token34 = 40

转载:C语言花里胡哨的宏的用法 - 知乎

我们偶尔会在代码里看到这样的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int Flower = 1;
int Fruit = 2;
int Vegetables = 3;
int GetFlowerId()
{
return Flower;
}
int GetFruitId()
{
return Fruit;
}
int GetVegetablesId()
{
return Vegetables;
}

三个函数在其他地方有调用,find一下声明又find不到。找来找去找到个类似声明的写法:

1
2
3
DeclareGetNameId(Flower);
DeclareGetNameId(Fruit);
DeclareGetNameId(Vegetables);

其宏函数的定义有可能是长这样的:

1
#define DeclareGetNameId(name) int Get##name##Id()

宏定义中,##表示字符串拼接,一般用于消除大面积的结构性重复比如上面的例子。

什么?你想把函数定义里的重复也消掉?也可以:

1
2
3
4
5
6
7
8
9
#define DefineGetNameId(name) \
int Get##name##id() \
{ \
return name; \
}

DefineGetNameId(Flower)
DefineGetNameId(Fruit)
DefineGetNameId(Vegetables)

这段代码还有重复,好吧定义里有一行可以直接用声明替换,再改改:

1
2
3
4
5
#define DefineGetNameId(name) \
DeclareGetNameId(name) \
{ \
return name; \
}

什么?调用点也想用这种花里胡哨的方法?行,给你一个:

1
#define GetNameId(name) Get##name##Id()

又重复了,好吧调用点用的宏和声明用的宏里面Get##name##Id出现了两次,看来得为这个东西单独写个宏了:

1
2
3
4
5
6
7
8
9
#define NameFactory(name) Get##name##Id
#define GetNameId(name) NameFactory(name)()

#define DeclareGetNameId(name) int GetNameId(name)
#define DefineGetNameId(name) \
DeclareGetNameId(name) \
{ \
return name; \
}

上面这段代码可以作为你的工具库,走到哪儿带到哪儿。什么?名字不通用?同时你还想返回别的类型?好吧我们改改:

1
2
3
4
5
6
7
8
9
#define NameFactory(name) Get##name
#define GetNameInst(name) NameFactory(name)()

#define DeclareGetNameInst(type, name) type GetNameInst(name)
#define DefineGetNameInst(type, name) \
DeclareGetNameInst(type, name) \
{ \
return name; \
}

这段代码已经做了非常多的消除重复的动作,终于可以作为程序员必备了吧?

下面我们验证一下。使用gcc -E x.c可以生成预处理文件,也就是把宏展开后生成一个文件,默认打在terminal,通过gcc -E x.c > x.i打印到x.i里,代码如下:

1
2
DeclareGetNameInst(int, Flower);
DefineGetNameInst(int, Flower)

展开如下:

1
2
int GetFlower();
int GetFlower() { return Flower; }

下面我们总结一下这个写法的优缺点:优点已经体现得比较清楚了,代码整洁,重复度低;缺点在本文开头也说过了,无法通过IDE的find定义、声明、调用功能进行代码阅读,代码阅读是无法流畅的。

下面谈一下具体工作中应该怎么选:宏函数这个东西,能不用则不用,毕竟安全性不太行。但凡事有个例外,假如3个同质函数可以忍,30个300个呢?太多就用吧。

延伸阅读(例子来源于多个帖子):

我们已经知道了##是用来进行拼接的。下面还有几个小知识点:

1、#表示字符串替换。网上常见的例子是这样

1
#define example(instr) printf("the input string is:\t%s\n",#instr);

example(abc);展开之后:

1
printf("the input string is:\t%s\n","abc");

2、#@表示字符替换。看两个小例子:

1
2
#define ToChar(x) #@x
#define ToString(x) #x

char a = ToChar(1);结果就是a=’1’;

char* str = ToString(123132);就成了str=”123132”;

宏定义。字符串拼接和字符串整形转字符串

样例

1
2
3
4
5
#define BMP280_GET_BITS(bitname, x)                    ((x & bitname##_MASK)>> bitname##_POS)
#define BMP280_STANDBY_DURN_MASK UINT8_C(0xE0)


conf->odr = BMP280_GET_BITS(BMP280_STANDBY_DURN, temp[1]);

C语言 define 定义函数(多行书写)

  • 待整理

C 语言:条件编译(#if,#ifdef,#ifndef,#endif,#else,#elif)

  • [C语言条件编译(#if,#ifdef,#ifndef,#endif,#else,#elif)](https://c.biancheng.net/view/449.html)
    指令 描述
    #define 定义宏
    #defined defined 可以出现在 #if 或 #elif 命令的条件中
    #include 包含一个源代码文件
    #undef 取消已定义的宏
    #ifdef [测试某个宏是否已被定义]如果宏已经定义,则返回真
    #ifndef [测试某个宏是否已被定义]如果宏没有定义,则返回真
    #if 如果给定条件为真,则编译下面代码
    #else #if 的替代方案
    #elif 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码
    #endif 结束一个 #if……#else 条件编译块
    #error 当遇到标准错误时,输出错误消息
    #pragma 使用标准化方法,向编译器发布特殊的命令到编译器中

defined 运算符

defined 运算符相对于 #ifdef 和 #ifndef 命令的优点是:你可以在更大型的预处理器表达式中使用它的值。如下例所示:

1
2
3
4
5
6
7
8
9
#if defined( __unix__ ) && defined( __GNUC__ )
/* ... */
#endif

#ifdef 标识符
#ifndef 标识符

#if defined 标识符
#if !defined 标识符
  • 大多数编译器会提供预定义宏,例如上例所使用的宏,它用来识别目标系统和编译器。因此,在 Unix 系统中,通常预先定义好了宏 __unix__,而 GCC 编译器则会预先定义好了宏 __GNUC__。类似地,微软 Windows 平台上的 Visual C 编译器会自动定义好宏 _WIN32 和宏 _MSC_VER。

条件编译

  • #if、#ifdef 或 #ifndef 等命令作为开头
1
2
3
4
5
6
7
8
9
10
#if 表达式1
[ 组1]
[#elif 表达式2
[ 组2]]
...
[#elif 表达式n
[ 组n ]]
[#else
[ 组n+1 ]]
#endif
  • 预处理器会依次计算条件表达式,直到发现结果非 0(也就是 true)的条件表达式。预处理器会保留对应组内的源代码,以供后续处理。如果找不到值为 true 的表达式,并且该条件式编译区域中包含 #else 命令,则保留 #else 命令组内的代码。

参数化的宏

CPP 一个强大的功能是可以使用参数化的宏来模拟函数。例如,下面的代码是计算一个数的平方:

1
2
3
int square(int x) {
return x * x;
}

我们可以使用宏重写上面的代码,如下:

1
#define square(x) ((x) * (x))

在使用带有参数的宏之前,必须使用 #define 指令定义。参数列表是括在圆括号内,且必须紧跟在宏名称的后边宏名称和左圆括号之间不允许有空格。例如:

实例

1
2
3
4
5
6
7
8
9
#include <stdio.h>

#define MAX(x,y) ((x) > (y) ? (x) : (y))

int main(void)
{
printf("Max between 20 and 10 is %d\n", MAX(10, 20));
return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

1
Max between 20 and 10 is 20