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

Jon*_*ert 53 c initialization

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

static const char* const stateNames[STATE_AMOUNT] =
{
    "Init state",
    "Run state",
    "Pause state",
    "Error state",
};
Run Code Online (Sandbox Code Playgroud)

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

如果缺少逗号,有没有办法为我的初始化生成编译器警告?

Dav*_*aro 63

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

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

如果您忘记逗号,您将收到类似于以下内容的编译错误: 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;    
}
Run Code Online (Sandbox Code Playgroud)

这就是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
Run Code Online (Sandbox Code Playgroud)

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


M.M*_*M.M 34

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

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" );
Run Code Online (Sandbox Code Playgroud)

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

作为奖励,当您添加新状态但忘记更新字符串表时,这也有帮助。但您可能也想研究 X 宏。


Moo*_*uck 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;
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

  • 静态断言似乎是一个更优雅的解决方案。我想您在静态断言作为语言的一部分实现之前就已经养成了这样做的习惯吗?与验证数组的预期大小的静态断言相比,您现在是否仍然认为它有任何优势? (4认同)
  • @CodyGray:是的,既然你提到了它,这就是预静态断言 (2认同)

Jon*_*caC 9

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

static const char* const stateNames[STATE_AMOUNT] =
{
      "Init state"
    , "Run state"
    , "Pause state"
    , "Error state"
};
Run Code Online (Sandbox Code Playgroud)

  • 在最后添加一些东西也更容易。您不必编辑上一行来添加逗号。(缺少逗号的主要原因。) (3认同)