C - 字符串数组范围的差异

时间:2010-09-23 14:28:25

标签: c

我练习了一个没有初始值的字符串数组。

尝试1

#include <stdio.h>

char *array[] = {};
int main(int argc, char *argv[]) {
    array[0]="Hello";
    array[1]="World";

    char **i = array;
    while (*i) {
        printf("%d %s\n", i, *i);
        i++;
    }
}

$ gcc array_of_strings.c&amp;&amp; ./a.out

6293704 Hello
6293712 World

工作正常。

尝试2

我以为我可以在主函数中移动数组指针。

#include <stdio.h>

int main(int argc, char *argv[]) {
    char *array[] = {};
    array[0]="Hello";
    array[1]="World";

    char **i = array;
    while (*i) {
        printf("%d %s\n", i, *i);
        i++;
    }
}

$ gcc array_of_strings.c&amp;&amp; ./a.out

-1899140568 (j͎?
-1899140560 World
-1899140552 ???%Y
-1899140544 1?I??^H??H???PTI???@
-1899140536 d?͎?
Segmentation fault
嗯,为什么不工作?它导致“分段错误”,输出难看。 有人可以解释为什么我不应该这样做吗?

5 个答案:

答案 0 :(得分:8)

您分配一个零元素的数组,然后添加两个指向它的指针。这会写入数组外部并导致缓冲区溢出。

顺便说一句,如果数组是全局分配的,它会覆盖未使用的内存,但是当它在main()中分配时会覆盖堆栈。

答案 1 :(得分:2)

在每种情况下,您都要分配一个空数组,然后尝试将项插入其中。 C不会对数组进行任何类型的调整;如果您将项目插入超出其长度的数组中,它将开始覆盖可能发生在数组之后的任何其他数据。在你的第一种情况下,当数组是全局的时候,你设法变得幸运,并且显然没有通过写过数组的末尾来破坏任何东西,而且幸运的是,在插入的两个数据之前有一个空值,所以你的循环终止在适当的地方。在第二种情况下,您碰巧覆盖了堆栈,堆栈用于存储局部变量,传递参数,返回值以及函数之间的返回位置。因此,写入和稍后读取,超过数组的末尾,会导致您在堆栈中写入,并读取随机无意义的值。

答案 2 :(得分:2)

两个问题。

  1. 您没有为数组项分配空间。使用空的初始化列表,您将分配一个空数组。当您写信至array[0]array[1]时,您正在写入您不拥有的记忆。

  2. 全局分配数组时,您会很幸运。全局(也称为静态分配)内存块往往用零填充。这对你有好处,因为你的while循环取决于它们是数组末尾的NULL指针。

    当您在堆栈上分配并访问超出数组末尾的内存时,您将获得已经在堆栈上发生的任何事情,这可能是任意垃圾。你的while (*i)循环没有得到它期望的NULL指针,所以它会继续读取垃圾数据,直到它找到一些看起来像NULL指针的零。

  3. 要修复#1,请为数组指定显式长度。要修复#2,必须在数组末尾显式添加NULL指针。

    char *array[3];
    array[0]="Hello";
    array[1]="World";
    array[2]=NULL;
    

    此外,对于它的价值,指针不能保证与int s的大小相同。最好使用%p打印指针而不是%d

    printf("%p %s\n", i, *i);
    

答案 3 :(得分:1)

在第二次尝试时执行它会在堆栈上声明数组,然后覆盖传递的参数,并在某个时刻覆盖函数末尾的返回地址。

它在第一次尝试中起作用的事实纯属巧合。你仍然可以覆盖记忆,但目前没有任何伤害。

答案 4 :(得分:0)

你不应该以两种方式做到这一点。在第一种情况下,你很幸运,你没有覆盖一些过程关键的内存,在第二种情况下,你破坏了堆栈。这两种情况都可能会随机崩溃,因为您正在写入您未预留的内存。

char *array[] = {};

这确实为零条目保留了内存,但是使用[0] = ...,您将一个元素写入您没有分配内存的位置。您应该阅读有关如何1)定义静态数组或2)动态分配数组的信息。