C语言中的字符串操作:指针与数组标记的区别

6

为什么第一个版本会导致程序崩溃,而第二个版本不会呢?它们不是同一件事吗?

指针表示法

char *shift = "mondo";
shift[3] = shift[2];

数组表示法

char shift[] = {'m', 'o', 'n', 'd', 'o', '\0'};
shift[3] = shift[2];

最小工作示例

int main( void )
{
    char *shift = "mondo";
    shift[3] = shift[2];

    char shift[] = {'m', 'o', 'n', 'd', 'o', '\0'};
    shift[3] = shift[2];

    return 0;
}

@user3121023 为什么?即使我没有使用 const - Nerva
@user3379939 - 这是由于历史原因,const 不是必需的。在 70 年代,人们并不知道更好的方法 - 与花之力和感恩死者有关。 - Ed Heal
@user3121023:像"mondo"这样的字符串字面量是以一种可写或不可写的方式分配的,这取决于平台。在您的情况下,它是不可写的,因此会出现错误。通常情况下,这种行为是未定义的;它可能工作,可能崩溃,也可能做其他事情。 - John Bode
@CoolGuy:最小工作示例。 - John Bode
5个回答

6
不!这是C语言中的一个重要问题。首先,你创建了一个指向只读内存部分的指针,也就是说,你不能改变它,只能读取它。其次,创建了一个字符数组,也就是一段连续字符的内存区域,你可以读取和写入其中的值,也就是既可以读取又可以更改数组的值。

3

第一个指向字符串常量(通常在代码的只读部分,应该是const char *,但由于历史原因,可以这样做)。

第二个创建一个数组,然后填充该数组。

因此它们不相同。


2

首先是在.TEXT段中分配内存,而第二个是将其放入.BSS中。.TEXT段中的内存实际上是只读或const

 char *string = "AAAA";

这将创建一个有效的const char *,因为内存将在.TEXT段中作为字符串文字分配。由于这通常被标记为只读,试图对其进行写操作将生成访问冲突或分段错误。
你想要做到这一点:
 char string[] = "AAAA";

这将按预期工作,并为四个大写A的字符串分配内存,并使用变量string作为指向该位置的指针。

1
抱歉,我不太明白你的意思。:P 如果您只想读取这些字符,则char *string =“String”是有效的。显然,如果您想更改此部分内存中任何字符的任何值,则必须使用数组(如您所示)或动态分配内存。 - zuko32
我会更新以澄清。 - David Hoelzer
好的,现在我明白你的意思了。但这是不对的。首先你分配了4个连续字节的一部分内存,然后在每个字节中存储值'A',十六进制为0x41,然后返回第一个字节的地址,而'string'是指向此地址的指针。只是你不能更改这个内存,它是只读的。如果你使用调试器,比如gdb,你就可以看到我的意思了。 - zuko32
你对 char *string = "AAAA"; 的解释完全不准确。尝试编译一些包含 char *string = "AAAA"; 的代码,并查看生成的汇编代码。它实际上是在内存中创建了一个字符串字面量 "AAAA"(通常在 .text 段中),然后将该字面量的地址赋值给指针。当尝试修改存储在只读内存中的值时,程序会崩溃。像 zuko32 说的那样,从该地址读取是可以的,但写入则不行。 - DaoWen
我会检查一下,这不是我理解的情况。 - David Hoelzer

1
这将创建一个指向现有字符串的指针:

char *shift = "mondo";

这将创建一个新的字符数组:

char shift[] = {'m', 'o', 'n', 'd', 'o', '\0'};

在第二种情况下,您可以修改字符,因为它们是您刚刚创建的。
在第一种情况下,您只是指向一个现有字符串,这个字符串不应该被修改。字符串存储的细节取决于特定的编译器。例如,它可以将字符串存储在不可修改的内存中。编译器还可以做一些技巧来节省空间。例如:
 char *s1 = "hello there";
 char *s2 = "there";

s2 可能实际上指向 s1 所指向的字符串中第七个位置上的字母 't'。

为了避免混淆,最好使用指向字符串常量的 const 指针:

const char *shift = "mondo";

这样,编译器会在您意外尝试修改时提醒您。

1
每当你使用char * str = "hello";定义一个字符串时,编译器会隐式地将其表达为const char * str= "hello";,这使得该符号进入程序内存的只读位置。
但是,在数组的情况下,相同的操作被解释为char const *array[];,因此当用户尝试更改数组的基地址时,编译器会发出警告。这是编译器隐式完成的。

1
你把 const 放在了 * 的错误一侧。 - Ed Heal
好的,我明白了你的意思。 - anshkun

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