如果缺少 const char* 数组初始化逗号,则生成编译器警告

Generate compiler warning if const char* array initialization comma is missing

我在我的 C 代码中经常使用字符串文字 tables。这些 table 都或多或少像这样:

static const char* const stateNames[STATE_AMOUNT] =
{
    "Init state",
    "Run state",
    "Pause state",
    "Error state",
};

上面代码的问题是如果 table 变长并且在开发过程中被修改,我有时会忘记一个逗号。代码编译没有问题,缺少逗号,但我的程序最终崩溃,因为最后一个字符串设置为 NULL。我使用了MinGW和Keil编译器来验证。

如果缺少逗号,是否有任何方法可以为我的初始化生成编译器警告?

将每个 const char* 括在一对括号中应该可以解决问题,如以下代码段所示:

static const char* const stateNames[5] =
{
    ("Init state"),
    ("Run state"),
    ("Pause state")     //comma missing
    ("Pause state3"),
    ("Error state")
};

如果您忘记逗号,您将得到类似于以下的编译错误:error: called object is not a function or function pointer

LIVE DEMO


请注意,如果您忘记了逗号,实际发生的情况是 C 实际上会将两个(或更多)字符串连接到下一个逗号或数组末尾。例如,假设您忘记了逗号,如下所示:

static const char* const stateNames[] =
{
    "Init state",
    "Run state",
    "Pause state" //comma missing
    "Pause state3" //comma missing
    "Error state"
};

int main(void)
{  
    printf("%s\n", stateNames[0]);
    return 0;    
}

这就是gcc-9.2 generates(其他编译器生成类似代码):

.LC0:
        .string "Init state"
        .string "Run state"
        .string "Pause statePause state3Error state" ; oooops look what happened
        .quad   .LC0
        .quad   .LC1
        .quad   .LC2
main:
        push    rbp
        mov     rbp, rsp
        mov     eax, OFFSET FLAT:.LC0
        mov     rdi, rax
        call    puts
        mov     eax, 0
        pop     rbp
        ret

很明显,最后三个字符串是连接在一起的,数组的长度不是您期望的长度。

您可以让编译器对数组进行计数,并在出现意外结果时生成错误消息:

enum { STATE_AMOUNT = 4 };

static const char* const stateNames[] =
{
    "Init state",
    "Run state",
    "Pause state"    // <--- missing comma
    "Error state",
};

_Static_assert( sizeof stateNames / sizeof *stateNames == STATE_AMOUNT,
        "oops, missed a comma" );

See this thread 如果您的编译器太旧且不支持,请提出实施的想法 _Static_assert

作为奖励,这也可以帮助您添加新状态但忘记更新字符串 table。但您可能也想查看 X 宏。

这不会让编译器帮助你,但我发现像下面这样写可以让人们更容易不漏掉逗号:

static const char* const stateNames[STATE_AMOUNT] =
{
      "Init state"
    , "Run state"
    , "Pause state"
    , "Error state"
};

我一直使用对明确大小的数组的引用来解决这个问题。

// no explicit size here
static const char* const stateNames[] =
{
    "Init state",
    "Run state",
    "Pause state",
    "Error state",
};
static const char* const (&stateNameVerifier)[STATE_AMOUNT] = stateNames;

http://coliru.stacked-crooked.com/a/593fc2eac80782a6

main.cpp:10:32: error: reference to type 'const char *const [5]' could not bind to an lvalue of type 'const char *const [4]'
static const char* const (&stateNameVerifier)[STATE_AMOUNT] = stateNames;