如果缺少 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
请注意,如果您忘记了逗号,实际发生的情况是 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;
我在我的 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
请注意,如果您忘记了逗号,实际发生的情况是 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;