无法修改C字符串

请考虑以下代码。

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

在我看来,这应该打印abcdexghij。 但是,它只是终止而不打印任何东西。

 int main(void){
     char * test =“abcdefghijklmnopqrstuvwxyz”;
     printf(“%s \ n”,test);
    返回EXIT_SUCCESS;
 }

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

接受的答案是好的,但不完全。

 char * test = "abcdefghijklmnopqrstuvwxyz"; 

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

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

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

 const char *test = "abcdefghijklmnopqrstuvwxyz"; 

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

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

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

 char test[] = "abcdefghijklmnopqrstuvwxyz"; 

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

在这种情况下, test本身是一个包含您指定的26个字符的数组,加上'\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() 。 非标准strdup()函数执行此操作(由POSIX定义,但不由ISO C定义)。

请注意, test可能是指针或数组,具体取决于您的声明方式。 如果将test传递给字符串函数,或传递给任何带有char*函数,这无关紧要,但是像sizeof test这样的函数将会有很大的不同,具体取决于test是指针还是数组。

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

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

不可修改:

 char * foo = "abc"; 

修改:

 char foo[] = "abc"; 

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

 const char* test = "abcdefghijklmnopqrstuvwxyz"; 

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

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

做:

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

strdup是一个可修改的副本。

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