修改指针指向的字符串是否有效?

时间:2009-03-02 22:45:18

标签: c

这是一个连接两个字符串的程序的简单示例。

#include <stdio.h>

void strcat(char *s, char *t);

void strcat(char *s, char *t) {
    while (*s++ != '\0');
    s--;
    while ((*s++ = *t++) != '\0');
}

int main() {
    char *s = "hello";
    strcat(s, " world");
    while (*s != '\0') {
        putchar(*s++);
    }
    return 0;
}

我想知道为什么会这样。在main()中,我有一个指向字符串“hello”的指针。根据K&amp; R书,修改这样的字符串是未定义的行为。那么为什么程序能够通过追加“世界”来修改它呢?或者是否附加不被视为修改?

9 个答案:

答案 0 :(得分:19)

未定义的行为意味着编译器可以发出执行任何操作的代码。工作是未定义的子集。

答案 1 :(得分:4)

我为MSN +1了,但至于为什么它有效,这是因为还没有任何东西来填补你的字符串背后的空间。声明一些变量,增加一些复杂性,你会开始看到一些古怪的东西。

答案 2 :(得分:2)

也许令人惊讶的是,您的编译器已将文字"hello"分配到读/写初始化数据而不是只读初始化数据。你的任务会破坏与该点相邻的任何东西,但是你的程序很小而且很简单,你看不到效果。 (把它放在for循环中,看看你是否在破坏" world"字面值。)

它在Ubuntu x64上失败,因为gcc将字符串文字放在只读数据中,当你尝试编写时,硬件MMU对象。

答案 3 :(得分:1)

这次你很幸运。
特别是在调试模式下,一些编译器会在声明周围放置备用内存(通常会填充一些明显的值),这样你就可以找到这样的代码了。

答案 4 :(得分:1)

它还取决于指针的声明方式。例如,可以更改ptr,以及ptr指向的内容:

char * ptr;

可以改变ptr指向的内容,但不能改变ptr:

char const * ptr;

可以改变ptr,但不能改变ptr指出的内容:

const char * ptr;

无法改变任何事情:

const char const * ptr;

答案 5 :(得分:1)

  

我想知道它为什么会起作用

没有。它会在Ubuntu x64上导致分段错误;要使代码工作,它不应只是work on your machine

将修改后的数据移到堆栈中可以解决linux中的数据区保护问题:

int main() {
    char b[] = "hello";
    char c[] = " ";
    char *s = b;

    strcat(s, " world");

    puts(b);
    puts(c);

    return 0;
}

虽然你只是安全,因为“世界”适合堆栈数据之间未使用的空间 - 将b更改为“hello to”并且linux检测到堆栈损坏:

*** stack smashing detected ***: bin/clobber terminated

答案 6 :(得分:1)

根据C99规范(C99:TC3,6.4.5,§5),字符串文字是

  

[...]用于初始化静态存储持续时间和长度的数组   足以包含序列。 [...]

这意味着它们具有类型char [],即原则上可以进行修改。为什么你不应该这样做在§6中解释:

  

如果这些数组的元素具有不同的数据,则未指定   适当的价值观如果程序试图修改这样的数组,则行为是   未定义。

具有相同内容的不同字符串文字可以 - 但不必 - 映射到相同的内存位置。由于行为未定义,编译器可以自由地将它们放在只读部分中,以便彻底失败,而不是引入可能难以检测的错误源。

答案 7 :(得分:0)

编译器允许你修改s,因为你不正确地将它标记为非const - 指向静态字符串的指针应该是

const char *s = "hello";

如果缺少const修饰符,您基本上已禁用安全性,这会阻止您写入不应写入的内存。 C几乎没有让你在脚下射击自己。在这种情况下,你很幸运,只有擦过你的小拇指。

答案 8 :(得分:0)

指向一些持有“hello”的内存,但并不打算包含更多内容。这意味着您很可能会覆盖其他内容。这是非常危险的,即使它似乎有效。

两个观察结果:

  1. * in * s--不是必需的。 s--就足够了,因为你只想减少价值。
  2. 您不需要自己编写strcat。它已经存在(你可能知道,但我告诉你的是: - ))。