字符串文字是常量吗?

peo*_*oro 22 c string const constants literals

如果我将一个字符串文字分配给a char*,即使使用了许多迂腐选项(-Wall -W -pedantic -std=c99),GCC和Clang都不会抱怨:

char *foo = "bar";
Run Code Online (Sandbox Code Playgroud)

而他们(当然)不要抱怨,如果我分配const char*char*.

这是否意味着字符串文字被认为是char*类型?他们不应该const char*吗?如果它们被修改,它就不是定义的行为!

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

Joh*_*itb 22

他们是类型的char[N]地方N是字符,包括终端的数量\0.所以是的,您可以将它们分配给它们char*,但您仍然无法写入它们(效果将是未定义的).

Wrt argv:它指向一个指向字符串的指针数组.这些字符串是明确可修改的.您可以更改它们,并且需要保留最后存储的值.

  • +1 - 请注意,这是他们的类型,但您必须将它们视为`const`(或未定义的行为/访问违规童话将支付您的访问) (8认同)
  • 自C89以来,C有'const`.它没有添加到C99. (6认同)
  • @Billy:难道你不觉得这有点不一致吗?否则,OP根本不需要问这个问题. (2认同)
  • @Vlad:我没有写标准:P (2认同)

Sha*_*our 9

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

[...]将一个字节或值为零的代码附加到由字符串文字或文字产生的每个多字节字符序列.然后使用多字节字符序列初始化静态存储持续时间和长度数组,数组足以包含序列.对于字符串文字,数组元素的类型为char,并使用多字节字符序列的各个字节进行初始化; [...]

所以这表示字符串文字具有静态存储持续时间(持续程序的生命周期),它的类型是char[](非char *),其长度是字符串文字的大小,附加为零.*第6段说:

如果程序试图修改此类数组,则行为未定义.

因此,尝试修改字符串文字未定义的行为,无论它们不是const.

关于argv在部分5.1.2.2.1 程序启动第2段说:

如果声明它们,main函数的参数应遵循以下约束:

[...]

- 参数argc和argv以及argv数组指向的字符串应由程序修改,并在程序启动和程序终止之间保留它们最后存储的值.

所以argv不被认为是一个字符串文字数组,可以修改其内容argv.


Jes*_*ter 6

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

warning: initialization discards qualifiers from pointer target type
Run Code Online (Sandbox Code Playgroud)

无论选项如何,GCC都会将文字放入只读内存部分,除非另有说明-fwritable-strings(但是此选项已从最近的GCC版本中删除).

命令行参数不是const,它们通常存在于堆栈中.


Aar*_*aid 5

(抱歉,我刚刚注意到这个问题被标记为c,而不是c++。也许我的答案与这个问题不太相关!)

字符串文字不完全是constor 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 *
}
Run Code Online (Sandbox Code Playgroud)

这两个数组的行为符合预期,证明foo能够告诉我们指针是否存在const。然后"hi"选择const版本foo。所以看起来这就解决了:文字是const......不是吗?

但是,如果删除的void foo( const char * )话就会变得很奇怪。首先,调用foo(arr_c)失败并在编译时出现错误。这是预期的。但文字调用 ( foo("hi")) 通过非常量调用来工作。

因此,文字比 ( 更 const ) arr_c,因为它们更喜欢衰减到const char *,与 不同arr_c。但是文字比 ( 更不 const ),因为如果需要,arr_cc它们愿意衰减到。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; }
Run Code Online (Sandbox Code Playgroud)

和之前一样,文字和 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);
}
Run Code Online (Sandbox Code Playgroud)

所以基本上,文字的行为const在任何时候都一样,除非您直接使用char *文字初始化 a 的特定情况。