16

人们通常会将“不可修改”与字面量相关联

char* str = "Hello World!";
*str = 'B';  // Bus Error!

然而,当使用复合文字时,我很快发现它们是完全可修改的(查看生成的机器代码,您会看到它们被推入堆栈):

char* str = (char[]){"Hello World"};
*str = 'B';  // A-Okay!

我正在用clang-703.0.29. 这两个示例不应该生成完全相同的机器代码吗?如果复合文字是可修改的,它真的是文字吗?

编辑:一个更短的例子是:

"Hello World"[0] = 'B';  // Bus Error!
(char[]){"Hello World"}[0] = 'B';  // Okay!
4

2 回答 2

17

复合文字是左值,其元素的值是可修改的。的情况下

char* str = (char[]){"Hello World"};
*str = 'B';  // A-Okay!  

您正在修改合法的复合文字。

C11-§6.5.2.5/4:

如果类型名称指定了一个未知大小的数组,则大小由 6.7.9 中指定的初始化列表确定,复合文字的类型是完整数组类型的类型。否则(当类型名称指定对象类型时),复合文字的类型是类型名称指定的类型。无论哪种情况,结果都是左值

可以看出复合字面量的类型是完整的数组类型并且是左值,因此它不像字符串字面量那样是可修改的

标准还提到

§6.5.2.5/7:

字符串文字和具有 const 限定类型的复合文字不需要指定不同的对象。101

此外,它说:

11 示例 4 可以通过以下结构指定只读复合文字:

(const float []){1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6}   

12 示例 5 以下三个表达式具有不同的含义:

"/tmp/fileXXXXXX"
(char []){"/tmp/fileXXXXXX"}
(const char []){"/tmp/fileXXXXXX"}

第一个始终具有静态存储持续时间并且具有类型数组char,但不需要修改;最后两个在函数体内出现时具有自动存储持续时间,而这两个中的第一个是可修改的

13 示例 6 与字符串文字一样,const 限定的复合文字可以放入只读内存中,甚至可以共享。例如,

(const char []){"abc"} == "abc"

如果文字的存储是共享的,可能会产生 1。

于 2016-04-17T11:59:42.380 回答
2

复合字面量语法是一种简写表达式,相当于带有初始化器的局部声明,后跟对由此声明的未命名对象的引用:

char *str = (char[]){ "Hello World" };

相当于:

char __unnamed__[] = { "Hello world" };
char *str = __unnamed__;

具有__unnamed__自动存储并被定义为可修改,可以通过str初始化指向它的指针进行修改。

在由char *str = "Hello World!";指向的对象的情况下str不应该修改。事实上,试图修改它具有未定义的行为。

C 标准可以将此类字符串文字定义为具有 typeconst char[]而不是char[],但这会在遗留代码中产生许多警告和错误。

然而,建议将标志传递给编译器以隐式生成此类字符串文字const并使整个项目const正确,即:将所有不用于修改其对象的指针参数定义为const. 对于gccclang,命令行选项是-Wwrite-strings。我还强烈建议启用更多警告并使用-Wall -W -Werror.

于 2019-11-23T10:12:37.843 回答