有编译器检查数组初始化符的数量(Have compiler check the number of

2019-08-17 09:11发布

初始化数组(在C ++中,但对于C工作将很可能在这里工作,以及任何解决方案),以较少的初始化,比它的元素是完全合法的:

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

然而,这可能是不明显的错误的来源。 有没有一种方法可以让编译器(GCC)检查初始化符的数量为一个特定的阵列,并且发出一个警告,或者即使申报与实际尺寸不符的错误?

我知道我可以使用int array[] = { 1, 2, 3 }; 然后可以使用包括静态断言sizeof(array)来验证我的期望存在。 但我使用array在其他翻译单元,所以我有一个明确的大小来声明它。 所以这招不会为我工作。

Answer 1:

(从作为请求评论晋升)

如果数组中的值是与系统的正确功能的重要,并在末端具有零初始化值会导致错误的话,我想补充一个单元测试,以验证该数组包含正确的数据,而不是试图强制执行它的代码。



Answer 2:

我有个主意。

#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 <tuple>

#define DECL_INIT_ARRAY(TYPE, NAME, COUNT, ...)                         \
  static_assert(COUNT ==                                                \
    std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::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]


Answer 3:

由于您使用的array在其他翻译单元,它显然有外部链接。 在这种情况下,你被允许申报两次,只要声明给它相同的类型。 所以,简单地声明它两次,第一次让编译器计数初始化,一旦指定大小。 将这个线路在一个源文件,声明任何报头的前面array

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

后来在同一个文件,把一个#include声明线array ,带线,如:

extern int array[10];

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



Answer 4:

我环顾四周,一个特定的答案,这在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);

如果的sizeof数组是你的预期,你可以很容易地检测。 如果静态断言失败编译错误将得到提升。 没有运行时开销。 静态断言的一个非常坚实的实现可以在这里找到: 静态断言执行Ç

为了您的方便:

#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

我加在这一个的顶部上的宏专门用于验证的sizeof阵列。 元件的数目必须指定的长度精确地匹配:

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

如果你不需要支持C99可以使用新的C11功能_Static_assert。 更多信息这里 。 如果你不需要C ++支持,您还可以依靠C ++静态断言:

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


文章来源: Have compiler check the number of array initializers