访问其指针从函数返回的结构中的二维数组时出现分段错误

Segmentation fault when accessing a 2D array in a structure whose pointer is returned from a function

我创建了一个有两个成员(intint**)的结构,并且我 return 将指向该结构的指针从一个函数指向 main()。可以访问结构中的 int 值。但是,在 main() 中,当我尝试访问二维数组的元素时,出现了 Segmentation fault : 11 。

#include<stdio.h>
#include<stdlib.h>

typedef struct Square {
    int value;
    int **array;
} Square;

Square * generate();

int main(int argc, char *argv[]){
    Square *sqrptr = generate();

    printf("%d\n", sqrptr -> value);
    /* It prints 1 */

    /* Print out the 2D array */
    for (int i = 0; i < 3; i++){
        for (int j = 0; j < 3 ; j++){
            printf("%d ", *(*((sqrptr -> array) + i) + j));
        }
        printf("\n");
    }
    /* It gives segmentation fault */

    return 0;
}

Square * generate(){
    Square mySquare;
    mySquare.value = 1;
    mySquare.array = malloc(sizeof(int*) * 3);

    /* Initialize the 2D array */
    for (int i = 0; i < 3; i++){
        *(mySquare.array + i) = malloc(sizeof(int) * 3);
        for (int j = 0; j < 3; j++){
            *(*(mySquare.array + i) + j) = 0;
        }
    }

    /* Print out the 2D array */
    for (int i = 0; i < 3; i++){
        for (int j = 0; j < 3l ; j++){
            printf("%d ", *(*(mySquare.array + i) + j));
        }
        printf("\n");
    }
    /* I can see the complete 2D array here */

    Square *sqrptr = &mySquare;

    return sqrptr;    
}

我尝试在 main() 中生成 Square,并使用该结构的一个指针来访问我的二维数组。它工作正常,所以我想当我使用来自其他函数的指针 returned 时我错过了一些东西。另一方面,我可以成功访问int value,所以我现在没有任何线索。

有人可以解释这个分段错误的根本原因吗?谢谢!

试试这个:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

typedef struct Square {
    int value;
    int **array;
} Square;

Square * generate();

int main(int argc, char *argv[]){
    Square *sqrptr = generate();

    printf("%d\n", sqrptr -> value);
    /* It prints 1 */

    /* Print out the 2D array */
    int i,j;
    for (i = 0; i < 3; i++){
        for (j = 0; j < 3 ; j++){
            printf("%d ", *(*((sqrptr -> array) + i) + j));
        }
        printf("\n");
    }
    /* It gives segmentation fault */

    return 0;
}

Square * generate(){
    Square* mySquare = (Square*) malloc(sizeof(Square)); //c++ compiler
    //Square* mySquare = (void*) malloc(sizeof(Square)); //c compiler
    mySquare->value = 1;
    mySquare->array = malloc(sizeof(int*) * 3);

    /* Initialize the 2D array */
    int i,j;
    for (i = 0; i < 3; i++){
        *(mySquare->array + i) = malloc(sizeof(int) * 3);
        for (j = 0; j < 3; j++){
            *(*(mySquare->array + i) + j) = 0;
        }
    }

    /* Print out the 2D array */
    for (i = 0; i < 3; i++){
        for (j = 0; j < 3l ; j++){
            printf("%d ", *(*(mySquare->array + i) + j));
        }
        printf("\n");
    }
    /* I can see the complete 2D array here */
    return mySquare;
}

您正在 return 指向局部变量 (&mySquare) 的指针。堆栈内存(局部变量所在的地方)是函数 returns 时,因此结果指针指向无效内存。分配结构,return指向堆内存的指针:

Square *my_square = malloc(sizeof *my_square);
//do stuff
return my_square;

或者传递一个指向堆栈变量的指针作为参数:

Square * generate(Square *my_square)
{
    //in case pointer wasn't provided, allocate
    if (my_square == NULL) {
        my_square = malloc(sizeof *my_square);
        if (!my_square)
            return NULL; // or exit or whatever
    }
    //initialize members. To initialize array to 3x3 zero matrix, you can use:
    for (int i=0;i<3;++i)
        my_square.array[i] = calloc(3, sizeof *my_square->array[i]);
    //or even, if you change array member to type int*:
    my_square.array = calloc(3*3, sizeof *my_square->array);
    //at the end:
    return my_square;
}

后者可以说是最灵活的解决方案:如果你想在堆栈上工作,你可以像这样调用函数:

Square my_stack_square;
generate(&my_stack_square);

如果需要使用堆内存,可以使用:

Square *my_heap_square = generate(NULL);

正如 Jonathan Leffler 指出的那样,对于像这样的小型结构,return按值计算成本并不算高。获取堆上的结构可以通过与 returning 任何其他类型相同的方式实现:

Square generate( void )
{
    Square my_square;
    //initialize
    return my_square;
}
//call like so:
Square sq = generate();

这里的想法是,您将在 generate 函数中使用一个局部变量来创建一个新方块,初始化字段,然后 return 它。因为在 C 中,一切都是通过值 传递的 ,这实质上意味着该函数会将局部变量的值从生成函数分配给调用者的作用域 sq 变量。对于像这样的小结构,这完全没问题。

此外,编译器通常要做的事情是将这些类型的函数优化为相当于我发布的第二个示例:本质上,您的函数将在堆栈内存中创建一个新的 Sqaure 对象来电者的。 可以发生,但并不是说一定会发生。这取决于编译时使用的优​​化级别,以及您正在 returning 的结构的大小。

基本上,如果您想使代码尽可能接近您现在拥有的代码,那么坚持第一个版本可能是最简单的(return使用堆指针)。

更灵活的方法是第二种方法(因为它允许您使用堆栈和堆,具体取决于您调用函数的方式)。

目前,使用第三种方法非常好:编译器很可能会将代码优化为最合理的代码。