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


53

我在C代码中经常使用字符串文字表。这些表或多或少看起来像这样:

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

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

如果缺少逗号,有什么方法可以为我的初始化生成编译器警告吗?


1
当您根本忘记为表添加状态时会发生什么?
Jeroen3

1
@ Jeroen3是的,这将导致相同的错误。使用静态断言针对STATE_AMOUNT测试列表长度也可以解决此问题。
乔尼·舒伯特

Answers:


62

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

现场演示


请注意,如果您忘记了逗号,则实际发生的情况是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生成的(其他编译器生成类似的代码):

.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

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


33

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

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" );

_Static_assert如果您的编译器很旧并且不支持,请参阅此线程以获取实现想法。

另外,当您添加新状态但忘记更新字符串表时,这也可以提供帮助。但是您可能也想研究X宏。


该死的...。这是我要输入的确切答案!
的焊工

11

我一直使用对显式大小的数组的引用来解决此问题。

// 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;

4
静态断言似乎是一种更为优雅的解决方案。我想您习惯在将静态断言作为语言的一部分实现之前这样做吗?您现在仍然看到此方法比验证数组的预期大小的静态断言有什么优势吗?
科迪·格雷

2
@CodyGray:是的,既然您提到它,这就是在声明前进行静态声明
Mooing Duck

9

这并不能使编译器对您有所帮助,但是我发现像下面这样编写它可以使人们更轻松地不使用逗号:

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

3
在末尾添加内容也更容易。您无需编辑前一行即可添加逗号。(缺少逗号的主要原因。)
datafiddler

@datafiddler:同意。在注释和取消注释时,对微调SQL SELECT命令中的列列表也很有用。您经常想更改最后一个;您很少要更改第一个。这样,您无需修改​​多行即可注释掉一项。
JonathanZ
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.