编程C:宏中的默认参数?
我正在写一个宏,它写一个结构{a,b,c,d,e}。 但是这个宏可以接收5个或更少的参数。如果接收的参数少于5个,则应将0放入结构中 我试图创建一个重复“0”N次的宏,但由于这N是参数的数量,它不是一个“硬编码”值,因此我无法将它与重复“0”的宏连接起来 我想知道是否有办法在宏本身上设置一个默认值编程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,
#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标准没有提到任何堆栈,