让编译器检查数组初始值设定项的数量

初始化一个数组(在C ++中,但任何适用于C的解决方案也可能在这里工作)使用较少的初始化器而不是元素是完全合法的:

int array[10] = { 1, 2, 3 }; 

但是,这可能是一些模糊的错误。 有没有办法让编译器(gcc)检查一个特定数组的初始值设定项的数量,如果声明和实际大小不匹配,则发出警告甚至错误?

我知道我可以使用int array[] = { 1, 2, 3 }; 然后可以使用涉及sizeof(array)静态断言来validation我的期望。 但我在其他翻译单元中使用array ,所以我必须用明确的大小声明它。 所以这个技巧对我不起作用。

(根据要求提出评论)

如果数组中的值对于系统的正确function很重要,并且在最后使用零初始化值会导致错误,那么我只需添加一个unit testing来validation数组是否包含正确的数据,而不是尝试强制执行它在代码中。

我有个主意。

 #define C_ASSERT(expr) extern char CAssertExtern[(expr)?1:-1] #define NUM_ARGS__(X, \ N64,N63,N62,N61,N60, \ N59,N58,N57,N56,N55,N54,N53,N52,N51,N50, \ N49,N48,N47,N46,N45,N44,N43,N42,N41,N40, \ N39,N38,N37,N36,N35,N34,N33,N32,N31,N30, \ N29,N28,N27,N26,N25,N24,N23,N22,N21,N20, \ N19,N18,N17,N16,N15,N14,N13,N12,N11,N10, \ N09,N08,N07,N06,N05,N04,N03,N02,N01, N, ...) N #define NUM_ARGS(...) \ NUM_ARGS__(0, __VA_ARGS__, \ 64,63,62,61,60, \ 59,58,57,56,55,54,53,52,51,50, \ 49,48,47,46,45,44,43,42,41,40, \ 39,38,37,36,35,34,33,32,31,30, \ 29,28,27,26,25,24,23,22,21,20, \ 19,18,17,16,15,14,13,12,11,10, \ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #define DECL_INIT_ARRAYN(TYPE, NAME, COUNT, N, ...) \ C_ASSERT(COUNT == N); \ TYPE NAME[COUNT] = { __VA_ARGS__ } #define DECL_INIT_ARRAY(TYPE, NAME, COUNT, ...) \ DECL_INIT_ARRAYN(TYPE, NAME, COUNT, NUM_ARGS(__VA_ARGS__), __VA_ARGS__) DECL_INIT_ARRAY(const int, array3_3, 3, 1, 2, 3); int main(void) { DECL_INIT_ARRAY(const int, array5_4, 5, 1, 2, 3, 4); DECL_INIT_ARRAY(const int, array5_6, 5, 1, 2, 3, 4, 5, 6); return 0; } 

输出( ideone ):

 prog.c: In function 'main': prog.c:33:3: error: size of array 'CAssertExtern' is negative prog.c:34:3: error: size of array 'CAssertExtern' is negative prog.c:34:3: error: excess elements in array initializer [-Werror] prog.c:34:3: error: (near initialization for 'array5_6') [-Werror] prog.c:34:3: error: unused variable 'array5_6' [-Werror=unused-variable] prog.c:33:3: error: unused variable 'array5_4' [-Werror=unused-variable] prog.c:34:3: error: unused variable 'CAssertExtern' [-Werror=unused-variable] cc1: all warnings being treated as errors 

UPD :OP发现了一个更短的C ++ 11解决方案,它基于使用__VA_ARGS__和静态/编译时断言的相同想法:

 #include  #define DECL_INIT_ARRAY(TYPE, NAME, COUNT, ...) \ static_assert(COUNT == \ std::tuple_size::value, \ "Array " #NAME " should have exactly " #COUNT " initializers"); \ TYPE NAME[COUNT] = { __VA_ARGS__ } DECL_INIT_ARRAY(const int, array3_3, 3, 1, 2, 3); int main(void) { DECL_INIT_ARRAY(const int, array5_4, 5, 1, 2, 3, 4); DECL_INIT_ARRAY(const int, array5_6, 5, 1, 2, 3, 4, 5, 6); return 0; } 

输出( ideone ):

 prog.cpp: In function 'int main()': prog.cpp:13:3: error: static assertion failed: Array array5_4 should have exactly 5 initializers prog.cpp:14:3: error: static assertion failed: Array array5_6 should have exactly 5 initializers prog.cpp:14:3: error: too many initializers for 'const int [5]' prog.cpp:13:3: warning: unused variable 'array5_4' [-Wunused-variable] prog.cpp:14:3: warning: unused variable 'array5_6' [-Wunused-variable] 

由于您在其他翻译单元中使用array ,因此它显然具有外部链接。 在这种情况下,只要声明为它提供相同的类型,就可以将它声明两次。 因此,只需将其声明两次,一次允许编译器计算初始值设定项并指定大小。 将此行放在一个源文件中,在声明array任何头之前:

 int array[] = { 1, 2, 3 }; 

稍后在同一个文件中,放入一个声明array#include行,其行如下:

 extern int array[10]; 

如果两个声明中的数组大小不同,编译器将报告错误。 如果它们相同,编译器将接受它们。

我在C99中四处寻找这个问题的具体答案,并在这里找到答案: 如何在预处理器宏中使用“sizeof”?

如果您没有定义数组的大小并使用:

 int test[] = {1,2} STATIC_ASSERT(sizeof(test)/sizeof(test[0]) == 3) /* with C11 support: */ _Static_assert(sizeof(test)/sizeof(test[0]) == 3) /* or more easily */ ASSERT_ARRAY_LENGTH(test, 3); 

您可以轻松检测数组的大小是否符合您的预期。 如果静态断言失败,将引发编译错误。 没有运行时开销。 可以在此处找到静态断言的非常可靠的实现: 静态断言实现C.

为了您的方便:

 #define ASSERT_CONCAT_(a, b) a##b #define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b) /* These can't be used after statements in c89. */ #ifdef __COUNTER__ #define STATIC_ASSERT(e,m) \ ;enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(int)(!!(e)) } #else /* This can't be used twice on the same line so ensure if using in headers * that the headers are not included twice (by wrapping in #ifndef...#endif) * Note it doesn't cause an issue when used on same line of separate modules * compiled with gcc -combine -fwhole-program. */ #define STATIC_ASSERT(e,m) \ ;enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(int)(!! (e)) } #endif 

我在这一个上面添加了一个专门用于validation数组大小的宏。 元素的数量必须与指定的长度完全匹配:

 #define ASSERT_ARRAY_LENGTH(array, length)\ STATIC_ASSERT(sizeof(array)/sizeof(array[0]) == length,\ "Array is not of expected length") 

如果您不需要支持C99,则可以使用新的C11function_Static_assert。 更多信息在这里 。 如果您不需要C支持,您还可以依赖c ++静态断言:

 std::size(test) == 3; /* C++ 17 */ (std::end(test) - std::begin(end)) == 3; /* C++ 14 */