编程C:宏中的默认参数?

编程C:宏中的默认参数?,c,macros,C,Macros,我正在写一个宏,它写一个结构{a,b,c,d,e}。 但是这个宏可以接收5个或更少的参数。如果接收的参数少于5个,则应将0放入结构中 我试图创建一个重复“0”N次的宏,但由于这N是参数的数量,它不是一个“硬编码”值,因此我无法将它与重复“0”的宏连接起来 我想知道是否有办法在宏本身上设置一个默认值 #define REPEAT1(s) s #define REPEAT2(s) s, s #define REPEAT3(s) s, REPEAT2(s) ... #define REPEATN(n,

我正在写一个宏,它写一个结构{a,b,c,d,e}。 但是这个宏可以接收5个或更少的参数。如果接收的参数少于5个,则应将0放入结构中

我试图创建一个重复“0”N次的宏,但由于这N是参数的数量,它不是一个“硬编码”值,因此我无法将它与重复“0”的宏连接起来

我想知道是否有办法在宏本身上设置一个默认值

#define REPEAT1(s) s
#define REPEAT2(s) s, s
#define REPEAT3(s) s, REPEAT2(s)
...
#define REPEATN(n, s) REPEAT ## n (s)
...
{__VA_ARGS__, REPEATN((5 - VA_ARGS_NUM(__VA_ARGS__)), 0)} 
// can't work because ## will not create a valid token with REPEAT and (5 - ....)

默认情况下,C将所有其他未初始化的设置为0

例如:

typedef struct {
    int a,b,c,d,e,f;
} myStruct;

myStruct str = {1, 2, 3};
现在您有了值
str.a=1、str.b=2和str.c=3
,而所有其他值都设置为
0


但是,如果您这样做:

myStruct str;
然后您不会将任何内容设置为
0
,特别是当这是堆栈上声明的局部变量时。
如果变量是全局变量,那么如果链接器在启动时将全局变量设置为
0
,则可以将其设置为
0

C默认将所有未初始化的其他变量设置为0

例如:

typedef struct {
    int a,b,c,d,e,f;
} myStruct;

myStruct str = {1, 2, 3};
现在您有了值
str.a=1、str.b=2和str.c=3
,而所有其他值都设置为
0


但是,如果您这样做:

myStruct str;
然后您不会将任何内容设置为
0
,特别是当这是堆栈上声明的局部变量时。 如果变量是全局变量,则可以将其设置为
0
,前提是链接器在启动时将设置全局变量的代码设置为
0

如果接收的参数少于5个,则应将0放入结构中

这可以通过使用复合文字来解决。首先,我们可以创建一个具有复合文字的宏来确定VA_参数的数量,因为没有用于此的标准宏:

#define ARGS_N(...) (sizeof((int[]){ __VA_ARGS__ }) / sizeof(int))
这可用于确定项目的数量。在本例中,如果正好有5个,则应使用这些值初始化结构,否则应将其初始化为0

然后,我们可以使用上面的宏创建条件初始值设定项列表。给定一些结构:

typedef struct
{
  int a;
  int b;
  int c;
  int d;
  int e;
} test_t;
我们可以通过自定义方法初始化该结构的实例:

test_t t1 = TEST_INIT(1,2,3); 
具体实施如下:

#define TEST_INIT(...) (ARGS_N(__VA_ARGS__)==5) ? (test_t){__VA_ARGS__} : (test_t){0}

不幸的是,一些编译器(gcc)会抱怨缺少初始值设定项,因为条件的两条路径都已展开,即使只计算了一条路径。在这种情况下,我们可以安全地告诉gcc闭嘴:

#pragma GCC diagnostic ignored "-Wmissing-field-initializers"

完整程序:

#include <stdio.h>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"

typedef struct
{
  int a;
  int b;
  int c;
  int d;
  int e;
} test_t;

#define ARGS_N(...) (sizeof((int[]){ __VA_ARGS__ }) / sizeof(int))

#define TEST_INIT(...) (ARGS_N(__VA_ARGS__)==5) ? (test_t){__VA_ARGS__} : (test_t){0}

int main (void)
{
  test_t t1 = TEST_INIT(1,2,3);  
  test_t t2 = TEST_INIT(1,2,3,4,5);  
  printf("%d %d %d %d %d\n", t1.a, t1.b, t1.c, t1.d, t1.e);
  printf("%d %d %d %d %d\n", t2.a, t2.b, t2.c, t2.d, t2.e);
}

#pragma GCC diagnostic pop
如果接收的参数少于5个,则应将0放入结构中

这可以通过使用复合文字来解决。首先,我们可以创建一个具有复合文字的宏来确定VA_参数的数量,因为没有用于此的标准宏:

#define ARGS_N(...) (sizeof((int[]){ __VA_ARGS__ }) / sizeof(int))
这可用于确定项目的数量。在本例中,如果正好有5个,则应使用这些值初始化结构,否则应将其初始化为0

然后,我们可以使用上面的宏创建条件初始值设定项列表。给定一些结构:

typedef struct
{
  int a;
  int b;
  int c;
  int d;
  int e;
} test_t;
我们可以通过自定义方法初始化该结构的实例:

test_t t1 = TEST_INIT(1,2,3); 
具体实施如下:

#define TEST_INIT(...) (ARGS_N(__VA_ARGS__)==5) ? (test_t){__VA_ARGS__} : (test_t){0}

不幸的是,一些编译器(gcc)会抱怨缺少初始值设定项,因为条件的两条路径都已展开,即使只计算了一条路径。在这种情况下,我们可以安全地告诉gcc闭嘴:

#pragma GCC diagnostic ignored "-Wmissing-field-initializers"

完整程序:

#include <stdio.h>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"

typedef struct
{
  int a;
  int b;
  int c;
  int d;
  int e;
} test_t;

#define ARGS_N(...) (sizeof((int[]){ __VA_ARGS__ }) / sizeof(int))

#define TEST_INIT(...) (ARGS_N(__VA_ARGS__)==5) ? (test_t){__VA_ARGS__} : (test_t){0}

int main (void)
{
  test_t t1 = TEST_INIT(1,2,3);  
  test_t t2 = TEST_INIT(1,2,3,4,5);  
  printf("%d %d %d %d %d\n", t1.a, t1.b, t1.c, t1.d, t1.e);
  printf("%d %d %d %d %d\n", t2.a, t2.b, t2.c, t2.d, t2.e);
}

#pragma GCC diagnostic pop

默认情况下C会这样做:任何元素少于预期的初始化都会用
0
s填充剩余的元素。也就是说:
struct foo bar={5}
相当于
struct foo bar={5,0,0,0}
@Kninnug:对于一般情况,这并不完全正确。整数初始化为
0
。其他类型分别设置为
0.0
(浮动)。空指针。后者都不一定与所有位的值相同。
0
@Olaf我的意思不是说它们将被0位填充,而是说元素将被设置为它们的等效值。IIRC对于
double
s,将
0
文本转换为
0.0
,对于此类类型,将null指针转换为
0.0
,以解决迭代宏的一般问题:@Kninnug:Ok。我只是想指出,对于其他初学者来说,这是
memset(…,0)
的主要区别(对于非整数数组来说,这几乎是无用的)。默认情况下,C会这样做:任何元素少于预期的初始化都会用
0
s填充剩余的元素。也就是说:
struct foo bar={5}
相当于
struct foo bar={5,0,0,0}
@Kninnug:对于一般情况,这并不完全正确。整数初始化为
0
。其他类型分别设置为
0.0
(浮动)。空指针。后者都不一定与所有位的值相同。
0
@Olaf我的意思不是说它们将被0位填充,而是说元素将被设置为它们的等效值。IIRC对于
double
s,将
0
文本转换为
0.0
,对于此类类型,将null指针转换为
0.0
,以解决迭代宏的一般问题:@Kninnug:Ok。我只是想指出,对于其他初学者来说,这是
memset(…,0)
的主要区别(对于非整数数组来说,这几乎是无用的)。它会引起警告(缺少字段初始值设定项),C99标准是指定0初始化还是编译器行为?第二部分并不完全准确。具有静态存储持续时间的变量,例如在文件范围中定义的变量,或在带有
static
限定符的函数中定义的变量,总是initialized@Kninnug带有
static
关键字的变量不会放在堆栈上,默认情况下不必设置为
0
。编译器/链接器的任务是添加代码,以便在启动时将RAM中的所有内容重置为
0
。通常,所有全局变量和静态变量都将设置为
0
,但这可以被禁用。@tilz0R标准没有提到任何堆栈,