【发布时间】: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,因此必须明确写出int2) 将参数定义放在函数签名之后,20 年前,大多数现代编译器仍然支持那个,但你应该把它写成:int main( int argc, char **argv) -
关于;
char *blank = " ";这会将blank指向的数组放入只读内存中,因此如果不引起段错误事件,则无法对其进行修改。 -
未定义的行为意味着它可以为所欲为,包括有时看似正常工作。