无法修改C字符串

考虑下面的代码。

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

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

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

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


被接受的答案很好,但不完整。

char * test = "abcdefghijklmnopqrstuvwxyz";

字符串文字是指一个类型为char[N]的静态存储持续时间(意味着它存在于程序的整个执行过程中)的匿名数组对象,其中N是字符串的长度加上终止''的长度。 这个对象不是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关键字不存在,要求在你的声明中使用const关键字可能会导致更安全的代码,但它会要求现有的代码是修改,ANSI委员会试图避免的东西,你应该假装字符串文字是const ,即使它们不是。如果你碰巧使用gcc, -Wwrite-strings选项会导致编译器将字符串文字视为const - 这使得gcc不合格。)

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

char test[] = "abcdefghijklmnopqrstuvwxyz";

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

在这种情况下, test本身是一个包含您指定的26个字符的数组,加上''终止符。 该数组的生存期取决于声明何处的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常见问题非常好。 第8部分涵盖字符和字符串,第8.5部分问题涉及问题1.32,它解决了您的具体问题。 第6节介绍了数组和指针之间经常令人困惑的关系。


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

不可修改:

char * foo = "abc";

修改:

char foo[] = "abc";

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

const char* test = "abcdefghijklmnopqrstuvwxyz";

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

链接地址: http://www.djcxy.com/p/12513.html

上一篇: Cannot modify C string

下一篇: Why isn't sizeof for a struct equal to the sum of sizeof of each member?