在 C 中编辑不可变字符串 - 与旧编译器一起使用,但与现代编译器中断
Posted
技术标签:
【中文标题】在 C 中编辑不可变字符串 - 与旧编译器一起使用,但与现代编译器中断【英文标题】:Editing Immutable Strings in C- worked with older compiler but breaks with modern one 【发布时间】:2020-08-15 07:20:47 【问题描述】:我正在处理一些在 Solaris 10 上运行的古老遗留代码。在旧服务器上,代码编译并运行没有问题。
然后将代码迁移到 Solaris 11 服务器,代码仍可在该服务器上编译,但在运行时会创建一个 seg 故障核心转储。
在这两种情况下,使用的编译器都是 /opt/SUNWspro/bin/cc。
这里是sn-p的代码:
#include <stdio.h>
char *blank = " ";
main(argc,argv)
int argc;
char **argv;
blank[35] = '\0';
printf("Success.\n");
这在 Solaris 10 上有效,但在 Solaris 11 上会导致分段错误(核心转储)。 通常我会说段错误是由于尝试写入空白[35]而导致空白[]数组仅上升到空白[34](它用35个空格字符初始化),除了此代码在Solaris上工作10.
另外,当我将行更改为 'blank[34] = '\0';'在新服务器上,我仍然得到一个段错误核心转储。
当我将空白更改为普通数组(并且还对主数组进行现代化改造)时,一切正常,正如我所期望的那样:
#include <stdio.h>
char blank[35];
int main(int argc,char **argv)
int i;
for (i=0; i<34; i++)
blank[i] = ' ';
blank[34] = '\0';
printf("Success.\n");
return 0;
我真正需要知道的是为什么这段代码在旧服务器上运行良好,我忽略了什么?我可以更改代码以使用普通数组使其在新服务器上运行,但这会导致什么样的问题?
【问题讨论】:
修改字符常量是否像未定义的行为? 在后来的 C 编译器中,像" ... "
这样的文字字符串被认为是不可变的。它可能在 Solaris 10 的一个古老版本的 C 中被允许。(在括号外声明参数类型的语法可以追溯到白垩纪时期。)
关于; main(argc,argv) int argc; char **argv;
1) 现代编译器不会假定返回类型为int
,因此必须明确写出int
2) 将参数定义放在函数签名之后,20 年前,大多数现代编译器仍然支持那个,但你应该把它写成:int main( int argc, char **argv)
关于; char *blank = " ";
这会将blank
指向的数组放入只读内存中,因此如果不引起段错误事件,则无法对其进行修改。
未定义的行为意味着它可以为所欲为,包括有时看似正常工作。
【参考方案1】:
他们是否使用完全相同版本的 C 编译器和完全相同的标志?
旧版本的 Studio 编译器 (/opt/SUNWspro/...) 默认将常量字符串放入可写内存中,除非您使用 -features=conststrings
标志将它们放入只读内存中。
更高版本的 Studio 编译器将 -features=conststrings
设为默认值,并要求 -features=no%conststrings
使其再次可写,如 https://docs.oracle.com/cd/E77782_01/html/E77788/bjapr.html#OSSCGbjaqo 的 Studio 12.6 文档中所示。
这类似于 gcc 对等效标志 -fwritable-strings
所做的事情,该标志在最旧的版本中默认开启,然后在
几个版本,在成为removed in gcc 4.0 之前,总是制作常量字符串
在只读存储器中。
【讨论】:
谢谢!使用-features=no%conststrings
标志重新编译代码使所有代码都能完美运行!这不仅解释了为什么代码过去可以工作而不再工作,而且提供了一个快速的解决方案——对我的 makefile 进行一次更改,现在所有代码都可以工作(这种不断变化的字符串文字废话发生在几个地方)。这就是我需要的答案。【参考方案2】:
请注意,您没有char blank[]
,而是char *blank
,它指向一个字符串文字。字符串文字是不可变的。此代码试图修改 blank
指向的文字字符之一,导致未定义行为。未定义行为的有趣之处在于它可以做任何事情,包括按照您的预期方式运行。
还值得注意的是,字符串文字已经隐式地以空值结尾,因此无论如何都不需要在末尾显式添加 '\0'。
【讨论】:
我完全同意,这就是为什么这段代码成功运行的事实让我感到莫名其妙。我的问题是为什么这段代码可以工作,而不是为什么它不工作(因为它不应该工作)。 未定义行为(假设它在 K&R 时代是未定义的;我对 ANSI/ISO 标准非常熟悉)可能会导致任何结果,包括按您的意愿工作的代码。【参考方案3】:很可能,问题不在于操作系统版本,而在于您构建代码时使用的编译器。这段代码有很多问题,很容易看出它在一个编译器上是如何工作的,而在另一个编译器上却不行。鉴于我们不知道您使用的是什么编译器,所以我不会推测。
但我敢打赌,如果机器具有相同的硬件架构(例如,两者都是 ultrasparc),那么如果你在 Solaris 10 上编译你的二进制文件,它在 Solaris 11 上运行得很好。
【讨论】:
以上是关于在 C 中编辑不可变字符串 - 与旧编译器一起使用,但与现代编译器中断的主要内容,如果未能解决你的问题,请参考以下文章
将新的 MS C++ 编译器与旧的 Visual Studio 一起使用