是否可以修改字符串文字和非字符串非复合文字?

Can a string literal and a non-string non-compound literal be modified?

字符串文字是左值,这为修改字符串文字打开了大门。

简而言之,来自 C:

In C source code, a literal is a token that denotes a fixed value, which may be an integer, a floating-point number, a character, or a string. A literal’s type is determined by its value and its notation. The literals discussed here are different from compound literals, which were introduced in the C99 standard. Compound literals are ordinary modifiable objects, similar to variables.

Although C does not strictly prohibit modifying string literals, you should not attempt to do so. For one thing, the compiler, treating the string literal as a constant, may place it in read-only memory, in which case the attempted write operation causes a fault. For another, if two or more identical string literals are used in the program, the compiler may store them at the same location, so that modifying one causes unexpected results when you access another.

  1. 第一段说"a literal in C denotes a fixed value"。

    • 这是否意味着不应修改文字(复合文字除外)?

    • 既然字符串字面量不是复合字面量,是否应该修改字符串字面量?

  2. 第二段说“C不严格禁止 修改字符串文字”,而编译器会这样做。字符串也应该如此 文字被修改?
  3. 这两段是否相互矛盾?我该如何理解?

  4. 既不是复合字面量也不是字符串字面量的字面量是否可以修改?

来自 C 标准(6.4.5 字符串文字)

7 It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined.

至于你的说法。

The second paragraph says that "C does not strictly prohibit modifying string literals" while compilers do. So should a string literal be modified?

然后编译器不修改字符串文字。它们可以将相同的字符串文字存储为一个数组。

正如 @o11c 在附件 J 的评论中指出的那样(资料性)可移植性问题 有写

J.5 Common extensions

1 The following extensions are widely used in many systems, but are not portable to all implementations. The inclusion of any extension that may cause a strictly conforming program to become invalid renders an implementation nonconforming. Examples of such extensions are new keywords, extra library functions declared in standard headers, or predefined macros with names that do not begin with an underscore.

J.5.5 Writable string literals

1 String literals are modifiable (in which case, identical string literals should denote distinct objects) (6.4.5).

不要修改字符串文字。将它们视为 char const[]。 字符串文字实际上是 char const[](修改它们会导致未定义的行为),但由于遗留原因,它们实际上是 char [],这意味着编译器不会阻止您写入它们,但您的程序会如果你这样做,仍然是未定义的。

更实际地说——并非每个硬件平台都提供机制来保护存储只读对象的内存位置。它必须被定义为 UB。有 3 个可能的选项:

  1. 文字(和更普遍的常量对象)保存在 RAM 中,但硬件不提供内存保护机制。没有什么可以阻止程序员写入此位置

  2. 文字(和常量对象)保存在 RAM 中,但硬件确实提供了内存保护机制 - 你会得到段错误

  3. Read Only 数据存储在只读存储器(例如uC FLASH)中。您可以尝试编写它,但它没有任何效果(例如 ARM)。没有引发硬件异常

  1. The first paragraph says that "a literal in C denotes a fixed value".
    • Does it mean that a literal (except compound literals) shouldn't be modified?

我不知道作者的意图是什么,但根据 C11/6.4.5p7"If the program attempts to modify such an array, the behavior is undefined.",在运行时由字符串文字产生的数组修改是公然未定义的

还应注意,尝试在运行时修改 const 限定的复合文字也会导致 未定义的行为 ,这与一些解释volatile 相关 C11/6.7.3p6 中的未定义行为。修改复合文字的定义很明确。

例如:

char *fubar = "hello world";
(*fubar)++; // SQUARELY UNDEFINED BEHAVIOUR!

char *fubar = (char[]){"hello world"};
(*fubar)++; // This is well defined.

在任何一段源代码中用 "goodbye galaxy" 字面上替换 "hello world" 都可以。然而,重新定义标准函数(即 #define memcpy strncpy#define size_t signed char,它们都是毁掉别人一天的好方法)是未定义的行为。

  • Since a string literal isn't a compound literal, should a string literal be modified?

由字符串文字产生的数组肯定在运行时被修改,因为任何这样做的尝试都会触发未定义的行为。

另一方面,字符串文字本身作为引用的字符序列存在于您的源代码中……当然,可以根据您的选择进行修改。不过,您没有义务修改它。

The second paragraph says that "C does not strictly prohibit modifying string literals" while compilers do. So should a string literal be modified?

C 标准并没有严格禁止很多未定义的行为;它留下 undefined 的行为,这意味着您的程序可能行为不稳定或不可移植。在定义明确的 C 领域,您的程序不应调用 任何 未定义行为,包括 溢出数组修改const-限定的对象或由字符串文字多线程等引起的竞争条件产生的数组

如果你想调用未定义的行为,C会让你搬起石头砸自己的脚。您可能有充分的理由这样做;也许你的程序会更优化,或者你的编译器 实际上允许你修改字符串文字 ("it's a feature, not a bug",他们说,"so give us your money",他们说,当你成为依赖于他们的非标准怪癖)。请注意,一些编译器会表现得好像没有发生尝试的修改,或崩溃,或者可能会导致一些漏洞。

...最重要的是,请注意您的代码将不再符合 C 代码!

Do the two paragraphs contradict each other?

也许是遗漏了。第一段确实指出值是固定的,第二段指出值 可能 在运行时通过调用未定义的行为是可修改的。

我认为作者的意思是区分源代码元素和运行时环境。例如,He/she 可以通过确保 字面量在运行时 期间 期间不应被修改来明确说明这一点。

How shall I understand them?

在 C 领域中,此类值不能在运行时更改,因为调用未定义的行为意味着相关代码不再符合 C 代码。

也许他们试图避免解释未定义的行为,因为解释起来似乎太复杂了。如果你深入研究这个主题,你会发现这个意思正如预测的那样,大致是这两个词的连词。

undefined: /ʌndɪˈfʌɪnd/ adj. not clear or defined. behaviour: /bɪˈheɪvjə/ noun. the way in which a machine or natural phenomenon works or functions

也就是说,在运行时尝试修改由字符串文字生成的数组会导致 "unclear functionality"。在计算机科学领域的任何地方都不需要记录,即使记录了,该记录也可能是谎言。

Can a literal which is neither compound literal nor string literal be modified?

作为源代码中的词法元素,只要它不覆盖标准符号,是的。不是 l-values(即没有任何存储)的文字,例如整数常量,显然不能在运行时修改。我想在某些系统上可能会尝试修改函数指针指向的内存,这可以看作是 literal;这也是未定义的行为,会导致代码不是 C。

也可以修改 C 标准未将其视为 对象 的许多其他类型的元素,例如 return 栈上的地址。这就是缓冲区溢出如此微妙危险的原因!