【预处理】——获取可变参数宏的参数数量

文章目录

    • 功能说明
    • 实现
      • `...` 的作用
      • `__VA_ARGS__` 的作用
      • `##__VA_ARGS__` 的作用
    • 解析
      • COUNT_ARGS(2, 4, 5)
      • 没有参数 COUNT_ARGS()
      • 参数大于 22 个
    • 示例

功能说明

用于获取可变参数宏实际传递了多少个参数。

  • COUNT_ARGS(1, 2, 3),填入了 3 个参数,返回值就是 3
  • COUNT_ARGS("Hello", 'a', 3.14, 4),填入了 4 个 参数,返回值就是 4
  • COUNT_ARGS(1),我们填入了 1 个参数,返回值就是 1

实现

#define __COUNT_ARGS(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, N, ...) N
#define COUNT_ARGS(...) __COUNT_ARGS(, ##__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
  1. __COUNT_ARGS 宏定义了一个带有多个参数的宏,其中 _0 到 _20 是占位符参数, N 表示参数个数。
  2. __COUNT_ARGS 宏使用了变参宏的特性,通过 ## 将参数序列转换为逗号分隔的参数列表。
  3. COUNT_ARGS 宏定义了一个变参宏,它使用 __COUNT_ARGS 宏来计算参数个数。
  4. COUNT_ARGS 宏通过添加一个空参数 (,) 来处理没有参数的情况,避免编译错误。
  5. COUNT_ARGS 宏最多可以接受 20 个参数,如果超过 20 个参数,则只会计算前 20 个参数的个数。

... 的作用

... 是一个可变参数宏的语法,它表示可以接受任意数量的参数。在宏定义中,... 表示参数列表的结束,可以在宏的定义中使用这些参数。

__VA_ARGS__ 的作用

  • __VA_ARGS__ 是一个特殊的宏,它表示可变参数的占位符。在宏定义中, __VA_ARGS__ 会被替换为传递给宏的实际参数。它可以用于将可变参数传递给其他宏或函数。

##__VA_ARGS__ 的作用

##__VA_ARGS__ 是在可变参数宏中使用的一个特殊语法。它的作用是在宏展开时,将可变参数的逗号连接符去除,以避免出现多余的逗号。这在某些情况下非常有用,特别是当宏的参数列表为空时。

当我们没有参数向 COUNT_ARGS 传递时,__VA_ARGS__ 的值为空,, ##__VA_ARGS__ 这部分会被删除。这里是使用了 ## 的语法,当 __VA_ARGS__ 为空字符串时,会连同最开始的 , 被一并删除了。

解析

#define __COUNT_ARGS(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, N, ...) N

这个宏要求至少传递 22 个参数,返回值就是这第 22 个参数。

如果超过第 22 个的参数,会被 … 给消化,并且返回第 22 个参数。

其实该宏的本质是先用 20, 19, ... 1, 0 占位,每多加一个参数,最右边的数就会被挤掉。

增加一个参数,0 就被挤掉,返回第 22 个参数就是 1
增加两个参数,0 和 1就被挤掉,返回第 22 个参数就是 2
以此类推

COUNT_ARGS(2, 4, 5)

COUNT_ARGS(2, 4, 5) 展开后就变为

__COUNT_ARGS(, 2, 4, 5, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

传递了 25 个参数给 __COUNT_ARGS ,接下来我们再与 __COUNT_ARGS 里的 形参 进行对应:

__COUNT_ARGS 宏的作用是返回实参的第 22 个参数,实际 __COUNT_ARGS 传递了 25 个参数 ,所以该宏只返回第 22 个参数即 3

没有参数 COUNT_ARGS()

COUNT_ARGS() 展开后就变为

__COUNT_ARGS(, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

有 22 个参数传递给 __COUNT_ARGS ,然后传递的参数与 __COUNT_ARGS 里的形参进行一一对应:
__COUNT_ARGS 宏的作用是返回实参的第 22 个参数,实际 __COUNT_ARGS 传递了 22 个参数 ,所以该宏只返回第 22 个参数即 0

参数大于 22 个

另外要注意传入的参数不能超过 22 个。如果传入的参数大于 22 个,那么这个宏将返回第 22 个参数的参数值。

例如

COUNT_ARGS(c)

宏展开后就是

(, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 46, 76, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

传递了 44 个参数给 __COUNT_ARGS ,接下来我们再与 __COUNT_ARGS 里的 形参 进行对应:

__COUNT_ARGS 宏的作用是返回实参的第 22 个参数,实际 __COUNT_ARGS 传递了 44 个参数 ,所以该宏只返回第 22 个参数即 46

所以当参数超过 22 个之后,不管参数的数量是多少,N 对应的永远是第 22 个参数的值。

示例

#include 

#define __COUNT_ARGS(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, N, ...) N
#define COUNT_ARGS(...) __COUNT_ARGS(, ##__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

int main(int argc, char *argv[])
{
    int count1 = COUNT_ARGS();                                                                              // count1 = 0
    int count2 = COUNT_ARGS(1, 2, 3);                                                                       // count2 = 3
    int count3 = COUNT_ARGS(1, 2, 3, 4, 5, 6, 7);                                                           // count3 = 7
    int count4 = COUNT_ARGS("Hello", 'a', 3.14);                                                            // count4 = 3
    int count5 = COUNT_ARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);         // count5 = 20
    int count6 = COUNT_ARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 46, 76); // count6 = 20

    printf("count1 = %d\n", count1);
    printf("count2 = %d\n", count2);
    printf("count3 = %d\n", count3);
    printf("count4 = %d\n", count4);
    printf("count5 = %d\n", count5);
    printf("count6 = %d\n", count6);

    return 0;
}

结果打印

count1 = 0
count2 = 3
count3 = 7
count4 = 3
count5 = 20
count6 = 46

你可能感兴趣的:(#,预处理,可变参数宏,获取宏参数的个数)