【问题标题】:Can a string literal and a non-string non-compound literal be modified? [duplicate]可以修改字符串文字和非字符串非复合文字吗? [复制]
【发布时间】:2017-08-14 23:22:07
【问题描述】:

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

简而言之,来自 C:

在 C 源代码中,文字是表示固定值的记号,可以是整数、浮点数、字符或细绳。文字的类型由它的值和符号决定。 这里讨论的文字不同于 C99 标准中引入的复合文字。复合文字是普通的可修改对象,类似于变量。

虽然 C 并不严格禁止修改字符串文字,但您不应尝试这样做。一方面,编译器将字符串文字视为常量,可能会将其放在只读内存中,在这种情况下,尝试的写入操作会导致错误。另一方面,如果程序中使用了两个或多个相同的字符串字面量,编译器可能会将它们存储在同一位置,这样当您访问另一个时,修改一个会导致意外结果。

  1. 第一段说“C 中的文字表示一个固定值”。

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

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

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

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

【问题讨论】:

  • 所有的书都是谎言。只相信标准。
  • 谢谢。我会记住这一点。我认为这本书紧跟标准,并以更易于理解的方式解释了标准。
  • 字符串字面量可以改变,但改变的结果是不确定的。
  • 相关阅读,我推荐这个:stackoverflow.com/questions/2397984/…
  • K&R C 没有关键字const,因此他们无法制作字符串文字const char

标签: c literals string-literals


【解决方案1】:

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

7 不确定这些数组是否不同,前提是它们的 元素具有适当的值。 如果程序试图 修改这样的数组,行为未定义。

至于你的说法。

第二段说“C并不严格禁止修改 字符串文字”,而编译器这样做。字符串文字应该是 修改了吗?

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

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

J.5 通用扩展

1 以下扩展被广泛用于 许多系统,但不能移植到所有实现。这 包含任何可能导致严格符合的扩展 程序变为无效会导致实现不合格。 此类扩展的示例是新关键字、额外的库函数 在标准头文件中声明,或名称为的预定义宏 不能以下划线开头。

J.5.5 可写字符串文字

1 字符串文字是可修改的(在这种情况下,相同的字符串 字面量应该表示不同的对象)(6.4.5)。

【讨论】:

  • 谢谢。既不是复合文字也不是字符串文字的文字是否被修改?
  • @Tim 正如“literal”这个名字所暗示的那样,它是一个未经修改的字面书写值。
  • 你的意思是既不是复合文字也不是字符串文字的文字不可修改?
  • @Tim 是的,我愿意。字面量是一个值。
  • @Tim 实际上 C 中的文字(字符串文字和复合文字除外)被命名为常量。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

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

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

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

    【讨论】:

      【解决方案4】:
      1. 第一段说“C 中的文字表示一个固定值”。
        • 这是否意味着不应修改文字(复合文字除外)?

      我不知道作者的意图是什么,但是根据C11/6.4.5p7“如果程序尝试修改这样的数组,行为未定义。”

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

      例如:

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

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

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

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

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

      第二段说“C 并不严格禁止修改字符串文字”,而编译器会这样做。那么字符串字面量应该修改吗?

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

      如果你想调用未定义的行为,C 会让你在脚下开枪。您可能有充分的理由这样做;也许您的程序会更优化,或者您的编译器实际上可以让您修改字符串文字(“这是一个功能,而不是错误”,他们说,“所以给我们你的钱”,他们说,当您依赖他们的非标准怪癖时)。请注意,某些编译器会表现得好像没有尝试修改,或者崩溃,或者可能会导致一些漏洞。

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

      这两段是否相互矛盾?

      也许是疏忽。第一段确实声明了这些值是固定的,第二段声明这些值可能可以在运行时通过调用未定义的行为来修改。

      我认为作者的意思是要区分源代码元素和运行时环境。例如,他/她可以通过确保明确说明文字不应在在运行时进行修改来简单地澄清这一点。

      我该如何理解它们?

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

      也许他们试图避免解释未定义的行为,因为它可能看起来太复杂而无法解释。如果您深入研究该主题,您会发现其含义正如预测的那样大致是两个词的结合。

      未定义:/ʌndɪˈfʌɪnd/ adj.不清楚或定义不明确。 行为:/bɪˈheɪvjə/ 名词。机器或自然现象的工作方式或功能

      也就是说,尝试在运行时修改由字符串文字生成的数组会导致“功能不明确”。在计算机科学领域的任何地方都不需要对其进行记录,即使记录在案,该文档也可能是谎言。

      既不是复合文字也不是字符串文字的文字可以修改吗?

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

      还可以修改许多其他类型的元素,这些元素不被 C 标准视为 对象,例如 堆栈上的返回地址。这就是缓冲区溢出如此危险的原因!

      【讨论】:

      • 你可能是想写(*fubar)++。可读性是为什么将 ++ 运算符与其他运算符结合起来总是一个坏主意的众多原因之一。
      猜你喜欢
      • 1970-01-01
      • 2018-08-04
      • 2011-03-08
      • 2010-10-12
      • 2018-08-29
      • 1970-01-01
      • 1970-01-01
      • 2017-06-12
      相关资源
      最近更新 更多