分段错误的原因与使用以下 C 代码返回随机值

Reason for Segmentation fault vs returning random values with the following C code

请解释(输出的原因)运行 两段代码的结果。也请解释一下它们的区别。 setArr(int, int) 有两个版本,如下所述...

#include <stdio.h>

void setArr(int, int);

int *arr[10]; // array of 10 int pointers

int main(int argc, char *argv[]) {
    int i;

    setArr(0, 0);
    setArr(1, 100);
    setArr(2, 200);
    setArr(3, 300);
    setArr(4, 400);

    for (i = 0; i < 5; i++) 
        printf("arr[%d]: %d\n", i, *arr[i]);   /* should be 0,100, 200,300,400 */

    return 0;
}

setArr

的版本

版本 A

void setArr(int index, int v) {
    int i = v; 
    *arr[index] = i;
}

输出: Segmentation fault (core dumped)

版本 B

void setArr(int index, int v) {
    int i = v;
    arr[index] = &i;
}

输出:

arr[0]: 400
arr[1]: 32748
arr[2]: 32748
arr[3]: 32748
arr[4]: 32748

我假设来自 运行 版本 B 的值只是随机值。

我对指针相当陌生,我了解 Java,所以请尽可能以初学者友好的方式对其进行解释 :)

版本 A 说 "the contents of an undefined pointer equals i" - 未定义的行为 = 崩溃。基本上你正在尝试写入内存中的某个未知位置。

版本 B 说 "Some pointer = some address" - 仍然是未定义的行为,因为 &i 超出了范围,但它仍然是一个地址,所以它 "kind of works"。在这里,您正在写入 "good" 个内存位置,但正在读取错误的内存位置。

您遇到了很多未定义的行为场景,但我会解释可能发生的情况。

arr 是一个包含 10 个整数指针的数组。

int * arr[10]; // array of 10 int pointers

当声明为全局变量时,所有这些指针都将是 zero-initialized - 因此,它是一个包含 10 个 NULL 指针的数组。

因此,版本 A 中的这一行取消了对地址 arr[index]:

的引用
* arr[index] = i;

实际上是在说:

*(NULL) = i;

那肯定会一直崩溃。

在版本 B 中,您将其设置为:

    int i = v;
    arr[index] = &i;

所以现在您正确地将指针分配给数组中的槽。然而,该地址被分配给局部堆栈变量 i,一旦函数 returns,它就超出范围。因此,当您在该地址打印值时,它肯定会被写在堆栈顶部的其他调用破坏。 (或者从技术上讲,"undefined behavior" 访问超出范围的堆栈变量的内存地址。)

更好:

void setArr (int index, int v){
    arr[index] = malloc(sizeof(int));
    *arr[index] = v;
}

上面为您要将该值复制到的地址分配了内存。如何释放该内存,您需要自己解决。

或者:

只需将 arr 声明为整数数组而不是指针:

int arr[10];
void setArr (int index, int v){
    arr[index] = v;
}

然后在 arr.

上没有 * 尊重的情况下正常打印
printf("arr[%d]: %d\n", i, arr[i]);

在第一种情况下,您已将 "array of pointers" 定义为整数。它们不是整数指针。在向它们存储任何值之前,您必须分配内存(最好使用 melloc/calloc 函数),或者您可以像这样定义整数数组: int (*a)[10]

以下 link 可能会让您对此有所了解:Difference between *ptr[10] and (*ptr)[10]

在第二种情况下,您将整数地址保存到整数指针中,这没问题,但是 int i 是函数 setArr() 的局部变量。因此,每次函数 setArr() 退出时,都会取消引用 int i 的值。因此,您将获得第二种情况的未定义行为。您可以使用 static int i 或使用全局变量(不是首选)或使用指向整数赋值的指针。