18

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

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

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

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

4

4 回答 4

7

(根据要求从评论中提升)

如果数组中的值对系统的正确功能很重要,并且最后初始化为零的值会导致错误,那么我只需添加一个单元测试来验证数组是否包含正确的数据,而不是尝试强制执行它在代码中。

于 2013-03-07T12:59:45.503 回答
5

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

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

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

extern int array[10];

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

于 2013-03-07T12:14:53.330 回答
4

我有个主意。

#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]
于 2013-03-07T11:55:57.427 回答
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);

您可以轻松检测数组的大小是否符合您的预期。如果静态断言失败,将引发编译错误。没有运行时开销。可以在这里找到一个非常可靠的静态断言实现: 静态断言实现 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

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

#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 */
于 2018-07-23T10:47:48.197 回答