无法修改C字符串

时间:2009-09-21 18:13:43

标签: c cstring

请考虑以下代码。

int main(void) {
    char * test = "abcdefghijklmnopqrstuvwxyz";
    test[5] = 'x';
    printf("%s\n", test);
    return EXIT_SUCCESS;
}

在我看来,这应该打印abcdexghij。但是,它只是在没有打印任何内容的情况下终止。

int main(void) {
    char * test = "abcdefghijklmnopqrstuvwxyz";
    printf("%s\n", test);
    return EXIT_SUCCESS;
}

然而,这很好用,所以我误解了操纵C字符串的概念吗?如果它很重要,我正在运行Mac OS X 10.6,它是我正在编译的32位二进制文​​件。

5 个答案:

答案 0 :(得分:28)

使用初始化值定义的字符指针进入只读段。要使它们可修改,您需要在堆上创建它们(例如使用new / malloc)或将它们定义为数组。

不可修改:

char * foo = "abc";

可修改:

char foo[] = "abc";

答案 1 :(得分:7)

This answer很好,但不太完整。

char * test = "abcdefghijklmnopqrstuvwxyz";

字符串文字是指类型为char[N]的匿名数组对象,具有静态存储持续时间(意味着它存在于整个程序执行中),其中N是字符串的长度加上终止'\0'的长度。此对象不是const,但任何修改它的尝试都有未定义的行为。 (实现可以使字符串文字可选,如果它选择,但大多数现代编译器不会。)

上面的声明创建了一个类型为char[27]的匿名对象,并使用该对象的第一个元素的地址来初始化test。因此像test[5] = 'x'这样的赋值会尝试修改数组,并且具有未定义的行为;通常它会使你的程序崩溃。 (初始化使用地址,因为文字是数组类型的表达式,在大多数上下文中隐式转换为指向数组第一个元素的指针。)

请注意,在C ++中,字符串文字实际上是const,上述声明是非法的。在C或C ++中,最好将test声明为指向 const char的指针:

const char *test = "abcdefghijklmnopqrstuvwxyz";

因此,如果您尝试通过test修改数组,编译器会发出警告。

(由于历史原因,C字符串文字不是const。在1989 ANSI C标准之前,const关键字不存在。要求它在像你这样的声明中使用会使更安全代码,但它需要修改现有的代码,这是ANSI委员会试图避免的。你应该假装字符串文字是const,即使它们不是。{If}碰巧使用gcc,-Wwrite-strings选项会导致编译器将字符串文字视为const - 这会使gcc不符合。)

如果您希望能够修改test引用的字符串,可以这样定义:

char test[] = "abcdefghijklmnopqrstuvwxyz";

编译器查看初始化程序以确定需要多大test。在这种情况下,test的类型为char[27]。字符串文字仍引用匿名的大多数只读数组对象,但其值已复制test。 (用于初始化数组对象的初始值设定项中的字符串文字是数组不会“衰减”到指针的上下文之一;其他是当它是一元&或{{1}的操作数时由于没有对匿名数组的进一步引用,编译器可能会对其进行优化。

在这种情况下,sizeof本身是一个包含您指定的26个字符以及test终止符的数组。该数组的生命周期取决于声明'\0'的位置,这可能或不重要。例如,如果您这样做:

test

调用者将收到指向不再存在的内容的指针。如果您需要引用定义char *func(void) { char test[] = "abcdefghijklmnopqrstuvwxyz"; return test; /* BAD IDEA */ } 的范围之外的数组,您可以将其定义为test,或者您可以使用static分配它:

malloc

因此,在您致电char *test = malloc(27); if (test == NULL) { /* error handling */ } strcpy(test, "abcdefghijklmnopqrstuvwxyz"; 之前,数组将继续存在。非标准free()函数执行此操作(由POSIX定义,但不由ISO C定义)。

请注意strdup()可能是指针或数组,具体取决于您的声明方式。如果你将test传递给一个字符串函数,或者传递给test的任何函数,这无关紧要,但char*之类的函数会有很大的不同,具体取决于{{1}是指针或数组。

comp.lang.c FAQ很棒。第8节涉及字符和字符串,问题8.5指向问题1.32,它解决了您的具体问题。第6节介绍了数组和指针之间经常混淆的关系。

答案 2 :(得分:4)

您应该养成将变量类型与初始化程序的类型相匹配的习惯。在这种情况下:

const char* test = "abcdefghijklmnopqrstuvwxyz";

这样您将收到编译器错误而不是运行时错误。将编译器警告级别提高到最大值也可以帮助避免此类陷阱。为什么这不是C中的错误可能是历史性的;早期编译器允许它并且禁止它可能在语言标准化时破坏了太多现有代码。但是现在操作系统不允许它,所以它是学术性的。

答案 3 :(得分:4)

字符串文字可能无法修改;最好假设他们不是。有关详细信息,请参阅here

答案 4 :(得分:0)

执行:

 char * bar = strdup(foo);
 bar[5] = 'x';

strdup制作了可修改的副本。

是的,您应该测试strdup没有返回NULL。