代码之家  ›  专栏  ›  技术社区  ›  fresskoma

无法修改C字符串

  •  8
  • fresskoma  · 技术社区  · 15 年前

    考虑下面的代码。

    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;
    }
    

    5 回复  |  直到 14 年前
        1
  •  32
  •   Jacob Relkin    14 年前

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

    不可修改:

    char * foo = "abc";
    

    可修改:

    char foo[] = "abc";
    
        2
  •  16
  •   Keith Thompson    6 年前

    This answer 很好,但不太完整。

    char * test = "abcdefghijklmnopqrstuvwxyz";
    

    字符串文字 引用类型为的匿名数组对象 char[N] N 是字符串的长度加上终止字符串的长度 '\0' const ,但任何修改它的尝试都有未定义的行为。(a)实施 可以 如果选择的话,使字符串文本可写,但大多数现代编译器不这样做。)

    上面的声明创建了类型为的匿名对象 char[27] ,并使用该对象的第一个元素的地址进行初始化 test . 这样的任务 test[5] = 'x'

    注意,在C++中,字符串文字实际上是 常数 测试 常数 char :

    const char *test = "abcdefghijklmnopqrstuvwxyz";
    

    因此,如果您试图通过修改数组,编译器将发出警告 测试 .

    常数 常数 关键字不存在。要求在像您这样的声明中使用它会使代码更安全,但它会要求修改现有代码,这是ANSI委员会试图避免的。你应该 假装 ,即使他们不是。如果您碰巧正在使用gcc,则 -Wwrite-strings 选项将导致编译器将字符串文本视为 常数

    char test[] = "abcdefghijklmnopqrstuvwxyz";
    

    测试 将是一种 字符[27] . 字符串文字仍然引用一个匿名的、大部分为只读的数组对象,但其值为 抄袭 测试 . (用于初始化数组对象的初始值设定项中的字符串文字是数组不“衰减”到指针的上下文之一;其他上下文是一元操作数 & sizeof )由于没有对匿名数组的进一步引用,编译器可能会对其进行优化。

    测试 它本身是一个数组,包含指定的26个字符,以及 终结者。该阵列的生存期取决于位置 声明,这可能重要,也可能不重要。例如,如果您这样做:

    char *func(void) {
        char test[] = "abcdefghijklmnopqrstuvwxyz";
        return test; /* BAD IDEA */
    }
    

    调用者将收到指向不再存在的内容的指针。如果需要引用作用域之外的数组 测试 已定义,您可以将其定义为 static malloc :

    char *test = malloc(27);
    if (test == NULL) {
        /* error handling */
    }
    strcpy(test, "abcdefghijklmnopqrstuvwxyz";
    

    因此,在调用之前,数组将继续存在 free() strdup() 函数执行此操作(它由POSIX定义,但不是由ISO C定义)。

    请仔细注意 测试 测试 到字符串函数,或任何接受 char* sizeof test 是指针或数组。

    comp.lang.c FAQ

        3
  •  4
  •   Community Lee Campbell    7 年前

    字符串文字可能不可修改;最好假设他们不是。看见 here 更多细节。

        4
  •  4
  •   Clifford    11 年前

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

    const char* test = "abcdefghijklmnopqrstuvwxyz";
    

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

        5
  •  0
  •   Peter Mortensen Mohit    14 年前

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

    strdup 制作可修改的副本。

    是的,你真的应该测试一下 标准 没有返回NULL。