在 C 中编辑不可变字符串 - 与较旧的编译器一起工作,但与现代编译器不兼容

Editing Immutable Strings in C- worked with older compiler but breaks with modern one

我正在使用一些在 Solaris 10 上 运行ning 的古老遗留代码。在旧服务器上,代码编译并且 运行 没有问题。

然后将代码迁移到 Solaris 11 服务器,代码仍然可以编译,但是当它 运行 时,它会创建段错误核心转储。

在这两种情况下,使用的编译器都是 /opt/SUNWspro/bin/cc。

这是代码片段:

#include <stdio.h>

char   *blank = "                                   ";

main(argc,argv)
int argc;
char **argv;
{

blank[35] = '[=10=]';
printf("Success.\n");

}

这在 Solaris 10 上有效,但在 Solaris 11 上会导致分段错误(核心转储)。 通常我会说段错误是由于当 blank[] 数组只上升到 blank[34](它用 35 space 个字符初始化)时尝试写入 blank[35] 引起的,除了这段代码在 Solaris 10 上工作。

此外,当我将行更改为 'blank[34] = '\0';'在新服务器上,我仍然得到段错误核心转储。

当我将 blank 更改为普通数组(并对 main 进行现代化)时,一切正常,如我所料:

#include <stdio.h>

char blank[35];

int main(int argc,char **argv)
{

  int i;

  for (i=0; i<34; i++)
  {
   blank[i] = ' ';
  }

  blank[34] = '[=11=]';
  printf("Success.\n");
  return 0;
}

我真正需要知道的是为什么这段代码 运行 在旧服务器上没问题,我忽略了什么?我可以更改代码以使用普通数组使其在新服务器上成为 运行,但这会导致什么样的问题?

请注意,您没有 char blank[],而是指向字符串文字的 char *blank。字符串文字是不可变的。此代码试图修改 blank 指向的文字的字符之一,导致未定义的行为。 Undefined Behavior 的有趣之处在于它可以做任何事情,包括按照您的预期运行。

还值得注意的是,字符串文字已经隐式以 null 终止,因此无论如何都不需要在末尾显式添加“\0”。

问题很可能不是 OS 版本,而是您构建代码所用的编译器。这段代码有很多问题,很容易看出它在一个编译器上如何工作,但在另一个编译器上却不行。鉴于我们不知道您使用的是什么编译器,我不打算推​​测。

但我敢打赌,如果机器具有相同的硬件架构(例如,都是 ultrasparc),那么如果您在 Solaris 10 上编译二进制文件,那么它 运行 在 Solaris 11 上会很好.

他们是否使用完全相同版本的 C 编译器和完全相同的标志?

旧版本的 Studio 编译器 (/opt/SUNWspro/...) 默认将常量字符串放入可写内存,除非您使用 -features=conststrings 标志将它们放入只读内存。

更高版本的 Studio 编译器将 -features=conststrings 作为默认值并要求 -features=no%conststrings 使它们再次可写,如位于 https://docs.oracle.com/cd/E77782_01/html/E77788/bjapr.html#OSSCGbjaqo.[=16= 的 Studio 12.6 文档中所示]

这类似于 gcc 使用等效标志 -fwritable-strings 所做的,该标志在最旧版本中默认打开,然后在 几个版本,在 removed in gcc 4.0 之前,总是制作常量字符串 在只读存储器中。