string文字是否为const?

即使在使用大量-Wall -W -pedantic -std=c99选项( -Wall -W -pedantic -std=c99 )时,GCC和Clang也不会抱怨,如果我将string文字分配给char*

 char *foo = "bar"; 

而他们(当然),如果我分配一个const char* char*来做抱怨。

这是否意味着string文字被认为是char*types? 他们不应该是const char*吗? 如果他们被修改,它没有被定义的行为!

和(一个不相关的问题)命令行参数(即: argv ):它被认为是一个string文字数组?

它们是char[N]types,其中N是包含终止\0的字符数。 所以是的,你可以将它们分配给char* ,但是你仍然不能写入它们(效果将是未定义的)。

Wrt argv :它指向一个指向string的指针数组。 这些string是明确可修改的。 你可以改变他们,他们需要保存最后存储的值。

使用-Wwrite-strings选项,您将获得:

 warning: initialization discards qualifiers from pointer target type 

不考虑这个选项,GCC会将文字放入只读存储器部分,除非通过使用-fwritable-strings (但是这个选项已经从最近的GCC版本中被删除)告知。

命令行参数不是常量,它们通常位于堆栈上。

为了完整起见, C99草案标准 ( C89和C11有类似的措辞 )在6.4.5 string文字第5段中说:

每个多字节字符序列附加一个字节或值为零的代码,该字符序列由一个或多个string组成。 然后使用多字节字符序列来初始化静态存储持续时间和长度数组,以便足以包含该序列。 对于string文字,数组元素的types为char ,并且用多字节字符序列的单个字节进行初始化; […]

所以这说的string文字有静态存储持续时间( 持续的程序的生命周期 ),它的types是char[] (而不是char * ),其长度是string文字的大小,并附加零。 *第6段说:

如果程序试图修改这样一个数组,行为是不确定的。

所以试图修改一个string文字是不确定的行为,不pipe它们不是const的事实。

关于5.1.2.2.1节中的argv 程序启动段落2说:

如果声明了,主函数的参数应该遵守以下约束条件:

[…]

– 参数argc和argv以及argv数组所指向的string应该可以被程序修改,并且在程序启动和程序终止之间保留它们的最后存储的值。

所以argv不被认为是一个string文字的数组,可以修改argv的内容。

(对不起,我只是刚刚注意到这个问题被标记为c ,而不是c++ 。也许我的答案毕竟不是那么相关的问题!)

string文字不是非常constnot-const ,对文字有一个特殊的奇怪规则。

摘要 :文字可以通过引用数组作为foo( const char (&)[N]) ,不能被当作非常量数组,他们宁愿腐烂const char * 。到目前为止,它看起来像是const但是有一个特殊的传统规则,允许文字衰减到char * ,参见下面的实验。

(在clang3.3上用-std=gnu++0x来完成实验,也许这是一个C ++ 11的问题?或者是clang的具体问题?无论哪种方式,都会出现一些奇怪的现象。)

起初,文字看来是const

 void foo( const char * ) { std::cout << "const char *" << std::endl; } void foo( char * ) { std::cout << " char *" << std::endl; } int main() { const char arr_cc[3] = "hi"; char arr_c[3] = "hi"; foo(arr_cc); // const char * foo(arr_c); // char * foo("hi"); // const char * } 

这两个数组的行为与预期的一样,表明foo能够告诉我们指针是否为const 。 然后"hi"selectfooconst版本。 所以看起来像是解决了这个问题:文字是const …不是吗?

但是 ,如果你删除void foo( const char * )那么它会变得奇怪。 首先,调用foo(arr_c)失败,并在编译时发生错误。 这是预料之中的。 但是文字调用( foo("hi") )通过非const调用工作。

所以,文字比arr_c更“常量”(因为他们喜欢衰减到const char * ,而不像arr_c 。但文字比arr_cc “更lessconst”,因为如果需要的话,他们愿意衰减到char *

(Clang在char *衰减时会发出警告)。

但是腐烂呢? 让我们避免它简单。

让我们把数组引用到foo来代替。 这给了我们更直观的结果:

 void foo( const char (&)[3] ) { std::cout << "const char (&)[3]" << std::endl; } void foo( char (&)[3] ) { std::cout << " char (&)[3]" << std::endl; } 

和以前一样,literal和const数组( arr_cc )使用const版本,非const版本由arr_c 。 如果我们删除foo( const char (&)[3] ) ,那么我们得到错误foo(arr_cc);foo("hi"); 。 总之,如果我们避免了指针衰减,而是使用了引用到数组,文字的行为就好像它们是const

模板?

在模板中,系统会推导出const char *而不是char *并且被“卡住”了。

 template<typename T> void bar(T *t) { // will deduce const char when a literal is supplied foo(t); } 

所以基本上,除了在直接使用文字初始化char *的特殊情况之外,字面的行为始终是const

约翰内斯的答案在types和内容上是正确的。 但除此之外,是的,修改string文字的内容是未定义的行为。

关于你关于argv的问题:

参数argc和argv以及argv数组所指向的string应该可以被程序修改,并且在程序启动和程序终止之间保留它们的最后存储的值。

它们是const char *,但是对于在const之前存在的遗留代码,将它们分配给char *有一个特定的排除。 命令行参数绝对不是字面的,它们是在运行时创build的。

在C89和C99中,string文字都是char *types(由于历史原因,据我所知)。 你是正确的,试图修改一个导致未定义的行为。 海湾合作委员会有一个特定的警告标志, 写string (这不是-Wall一部分),这将警告你,如果你尝试这样做。

至于argv ,参数被复制到程序的地址空间中,并且可以在main()函数中安全地修改。

编辑 :哎呀,有-Wno-write-strings意外复制。 更新了警告标志的正确forms。

string文字具有正式typeschar []但是语义types为const char [] 。 纯粹主义者讨厌它,但这通常是有用的和无害的,除了带来大量的新手到“为什么我的程序崩溃?!?! 的问题。