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文字不是非常const
或not-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"
selectfoo
的const
版本。 所以看起来像是解决了这个问题:文字是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 []
。 纯粹主义者讨厌它,但这通常是有用的和无害的,除了带来大量的新手到“为什么我的程序崩溃?!?! 的问题。