为什么这个简单的字符串赋值会导致分段错误?

4

我有如下的代码:

#include <iostream>
using namespace std;
int main()
{
    char* a = "foo";
    char* b = "bar";
    a = b;
    cout << a << ", " << b << endl;
    return 0;
}

这段代码可以编译并运行,即打印出bar,bar。现在我想证明这里发生的不是字符串复制。我想改变b并展示a也会随之改变。我想出了这个简单的代码:

#include <iostream>
using namespace std;
int main()
{
    char* a = "foo";
    char* b = "bar";
    a = b;
    b[1] = 'u'; // ← just this line added
    cout << a << ", " << b << endl;
    return 0;
}

…但它会崩溃。为什么?有趣的是,以下修改可以正常运行:

#include <iostream>
using namespace std;
int main()
{
    char* a = "foo";
    char b[] = "bar"; // ← declaration changed here
    a = b;
    b[1] = 'u';
    cout << a << ", " << b << endl;
    return 0;
}

为什么它不像以前那样出现段错误?我猜我漏掉了指针风格和数组风格字符串初始化之间的一些重要区别。
6个回答

9

还要注意,您的代码会产生编译器警告,解释了这个问题:“警告:从字符串常量转换为‘char*’是不推荐的”。 - schnaader
g++ -Wall -pedantic 4.01没有警告,我该打开什么来获取警告? - zoul
在Debian上使用g++ 4.3.2,参数不重要,只需调用“g++ test.cpp”即可,添加-Wall,-pedantic或两者都不会改变任何内容。看起来这个警告是在4.1/4.2之后添加的。 - schnaader
谢谢,呃,谢谢您。 - zoul
注:许多旧的编译器和/或执行环境可能会让你做这件事(引入一些有趣的可能错误),因此你会时不时地看到它。 - dmckee --- ex-moderator kitten

6

When you write this:

char *b = "bar";

编译器分配了一个匿名的(无名称)内存区域来存储字符串常量“bar”。字符串常量不可修改,因此编译器(借助链接器和操作系统的帮助)将字符串常量放在运行程序的内存空间中的一个写保护部分。当您尝试修改它时,操作系统会捕捉到并导致您的程序出现分段错误。(您的代码是C++而不是C,但这与本问题无关。)

2
您可以通过打印指针的值来显示已更改 'a' 的情况。
#include <iostream>
using namespace std;
int main()
{
    char* a = "foo";
    char* b = "bar";
    a = b;

    cout << (void*)a << ", " << (void*)b << endl;
}

这将打印出'a'和'b'指向的地址。
你需要将其转换为'void*',因为运算符'<<'被重载为打印字符串的'char*',任何其他指针都会打印地址。


2
当你编写代码时:
char *foo = "bar";

实际上,“bar”被存储在内存的只读段中,因此它是不可变的。你尝试修改一个只读段导致了段错误。

1
理论上,字符串字面值不应该被赋值给char*,只能是'const char*'。这样编译器就会在你写出段错误代码之前阻止你。

-1

这种差异可能是与编译器有关的。为了证明你的观点,使用malloc来分配缓冲区,然后将字符串复制到该缓冲区中,并在不再需要该字符串时不要忘记使用free。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接