这是一个愚蠢的问题,但我似乎无法做到这一点。我遇到了另一个question,但给出的答案没有正确解决我特定用例中的警告。
我正在尝试声明一个常量字符串数组在posix_spawn
函数中作为argv
传递,但GCC抱怨const
被丢弃。请参阅以下示例代码:
#include <stdio.h>
/* Similar signature as posix_spawn() shown for brevity. */
static void show(char *const argv[])
{
unsigned i = 0;
while(argv[i] != NULL) {
printf("%s\n", argv[i++]);
}
}
int main(void)
{
const char exe[] = "/usr/bin/some/exe";
char *const argv[] = {
exe,
"-a",
"-b",
NULL
};
show(argv);
return 0;
}
将其编译为:
gcc -std=c89 -Wall -Wextra -Wpedantic -Wwrite-strings test.c -o test
test.c: In function ‘main’:
test.c:17:9: warning: initializer element is not computable at load time [-Wpedantic]
exe,
^
test.c:17:9: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
test.c:18:9: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
"-a",
^
test.c:19:9: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
"-b",
^
因为exe
本身就是一个常量字符串,如"-a"
和"-b"
,我认为这是正确的。但海湾合作委员会似乎不同意。从数组中删除exe
并删除-Wwrite-strings
编译而不发出警告。也许我错过了一些太基础的东西。
如何声明const数组的字符串?
答案 0 :(得分:1)
声明char *const argv[]
使argv
成为 mutable char
的常量指针数组。创建数组后,您无法更改指针指向的位置(您不能说argv[0] = "/bin/some_other_program"
),但您可以自行更改字符(argv[1][1] = 'b'
,以便通过{-b
{1}}代替)。
但是,您指定为argv
元素的指针是指向常量char
的指针。即使argv
的类型允许你,实际执行任何这些分配也是未定义的行为,因此警告。由于您无法更改函数的类型,因此您需要以某种方式摆脱const
的问题。选项是使用strdup
,将字符串放入char[]
变量(隐式地复制它们),或者抛弃const
这个级别(如果程序实际上是UB)修改值但如果不安全则应该是安全的。 Here是使用后两种方法的示例。
答案 1 :(得分:0)
有问题的函数posix_spawn
需要一个指向非const char
指针数组(第一个元素)的指针。尽管如此,该函数不会修改字符串。
由于历史原因,C API中的const-correctness不良的情况并不少见。
您可以在标准C中没有任何警告的情况下将字符串文字传递给它,因为字符串文字的类型为:非const char数组。 (修改它们是未定义的行为,无需诊断)。
如果您使用-Wwrite-strings
标志尝试获取有关潜在UB的警告,那么对于与非const-correct API交互的情况,它会给您这些误报。
在C89中使用API的预期方法是使用:
char exe[] = "/usr/bin/some/exe";
char * argv[] = {
NULL,
"-a",
"-b",
NULL
};
argv[0] = exe;
show(argv);
请注意,在C89中,您的数组不可能是const
,也可以使用局部变量exe
进行初始化。这是因为C89要求支撑列表中的所有初始值设定项都是常量表达式,其定义不包括局部变量的地址。
如果您想使用-Wwrite-strings
,则可以使用(char *)"-a"
代替"-a"
来取消警告,等等。
在评论中建议使用const char *
作为字符串类型然后使用强制转换,如下所示:
const char exe[] = "/usr/bin/some/exe";
const char * argv[] = {
NULL,
"-a",
"-b",
NULL
};
argv[0] = exe;
show((char **)argv);
但是,这可能违反严格别名规则。即使规则允许将const T
别名为T
,此规则也不会“递归”;可能不允许将const char *
别名为char *
,尽管该规则的措辞并非100%明确。 See this question for further discussion
如果你想提供一个接受const char **
并调用posix_spawn
并且不违反C标准的包装器,那么你实际上必须将指针列表的副本复制成{ {1}}。 (但您不必实际复制字符串内容)