我练习了一个没有初始值的字符串数组。
#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
工作正常。
我以为我可以在主函数中移动数组指针。
#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
嗯,为什么不工作?它导致“分段错误”,输出难看。
有人可以解释为什么我不应该这样做吗?
答案 0 :(得分:8)
您分配一个零元素的数组,然后添加两个指向它的指针。这会写入数组外部并导致缓冲区溢出。
顺便说一句,如果数组是全局分配的,它会覆盖未使用的内存,但是当它在main()中分配时会覆盖堆栈。
答案 1 :(得分:2)
在每种情况下,您都要分配一个空数组,然后尝试将项插入其中。 C不会对数组进行任何类型的调整;如果您将项目插入超出其长度的数组中,它将开始覆盖可能发生在数组之后的任何其他数据。在你的第一种情况下,当数组是全局的时候,你设法变得幸运,并且显然没有通过写过数组的末尾来破坏任何东西,而且幸运的是,在插入的两个数据之前有一个空值,所以你的循环终止在适当的地方。在第二种情况下,您碰巧覆盖了堆栈,堆栈用于存储局部变量,传递参数,返回值以及函数之间的返回位置。因此,写入和稍后读取,超过数组的末尾,会导致您在堆栈中写入,并读取随机无意义的值。
答案 2 :(得分:2)
两个问题。
您没有为数组项分配空间。使用空的初始化列表,您将分配一个空数组。当您写信至array[0]
和array[1]
时,您正在写入您不拥有的记忆。
全局分配数组时,您会很幸运。全局(也称为静态分配)内存块往往用零填充。这对你有好处,因为你的while
循环取决于它们是数组末尾的NULL指针。
当您在堆栈上分配并访问超出数组末尾的内存时,您将获得已经在堆栈上发生的任何事情,这可能是任意垃圾。你的while (*i)
循环没有得到它期望的NULL指针,所以它会继续读取垃圾数据,直到它找到一些看起来像NULL指针的零。
要修复#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)动态分配数组的信息。