C中的矩阵指针

Matrix pointer in C

我会直接问我要问的问题,我也看到了一些类似的问题,但不是我要找的问题...所以看来我必须在新论坛上提问。 我正在为将来的考试做准备,那里不需要指针,但我想获得一些额外的信息和能力。 这是问题后面的代码...... 我正在使用 Fedora 33,我知道它不同于 Windows 上的某些 IDE(例如:Visual Studio 或 Dev C++)

/* It's just a simple test, if this work I will get myself into a more complicated one, as you could read in the 
 * forum, I'm getting ready ( just a recheck of my abilities ) for an universitary examinaton. */
#include <stdio.h>
#include <stdlib.h>
#define N 5

void casual_generation(int** mat);
void prompt_print(int** mat);

int main()
{
    int **mat[N][N];
    casual_generation(**mat);
    prompt_print(**mat);
}

void casual_generation(int** mat)
{
    int i=0,j=0;
    
    for(i=0;i<N;i++)
        for(j=0;j<N;j++)
            mat[i][j] = rand() % 50;
}

void prompt_print(int** mat)
{
    int i=0,j=0;
    for(i=0;i<N;i++)
    {
        for(j=0;j<N;j++)
            printf("%d  ", mat[i][j]);
        printf("\n");
    }
}

论坛上的其他人使用了 malloc、struct 或其他东西,正如您在这张图片中看到的,当我尝试执行他时,它显示“分段错误(核心已转储)” screen error 我的错误在哪里? 如果你愿意,你能把带有传递值指针的版本发给我吗? 感谢谁能给我答案,并付出时间。

你本来想做的

Int mat[N][N];

casual_generation(mat);
prompt_print(mat);

通过传递 **mat,你传递的是 mat[0][0],这是一个 int,但你想传递整个矩阵,它是一个指向 int 的指针(即 int **)

您可能想在代码中引入 srand()

澄清一下:

matint ** 类型并且它是整个矩阵(或者如果你想要它是指向第一行的指针)

*matint * 类型并且它是矩阵的第一行(或者如果你想要它是指向第一行第一个元素的指针)

**matint 类型,它是矩阵第一行的第一个元素

Where is my error?

发生“分段错误”错误是因为您将变量 mat 定义为指针,但没有为其分配任何内存。

 int **mat[N][N]; 

这个声明

int **mat[N][N];

没有意义。这意味着您有一个矩阵元素,其元素是 int ** 类型的指针。但是您需要一个矩阵元素,其元素是 int 类型的整数。那就是你需要这样的声明

int mat[N][N];

现在你有了一个二维整数数组(或矩阵)。

当您要将此二维数组传递给函数,然后将其用作参数表达式时,它会转换为指向类型为 int ( * )[N].

的第一个元素的指针

相应地,接受这样一个数组的函数应该声明为

void casual_generation( int mat[][N], size_t n );
void prompt_print( int mat[][N], size_t n );

或(完全等价)像

void casual_generation( int ( * mat )[N], size_t n );
void prompt_print( int ( *mat )[N], size_t n );

因为编译器将具有数组类型的函数参数调整为指向数组元素类型的指针。

现在,例如第一个函数可以按以下方式定义

void casual_generation( int ( * mat )[N], size_t n )
{
    for ( size_t i = 0; i < n; i++ )
    {
        for ( size_t j = 0; j < N; j++ )
        {
            mat[i][j] = rand() % 50;
        }
    }
}

而且函数可以这样调用

casual_generation( mat, N );

类似的方法可以定义函数prompt_print.

使用第二个参数使函数更通用。例如对于不同行数的二维数组可以调用。

现在我将解释为什么您的原始代码中出现分段错误。

你有这份声明

int **mat[N][N];

int **.

类型的二维指针数组

那么您正在使用表达式 **mat 作为函数调用的参数,例如

casual_generation(**mat);

然后您正在应用取消引用运算符,如 *mat,数组指示符被转换为指向其第一个元素(行)的指针,类型为 int ** ( * )[N]。所以取消引用这个指针你得到你的数组的第一行int **[N]。第二次将解除引用的运算符应用于此具有数组类型的表达式,所使用的表达式再次转换为指向其类型 int **( * ) 的第一个元素的指针。即指向原二维数组第一行的第一个元素。取消引用此指针,您将获得 int ** 类型的第一个元素。这个具有不确定值的未初始​​化指针被函数接受为参数。

因此在函数中取消引用原始矩阵的第一个未初始化元素

mat[i][j] = rand() % 50;
   ^^^

你遇到了分段错误。错误的原因是不正确的矩阵和相应的函数参数,如上面答案开头所示。

int **mat[N][N];

在这里,您定义了一个指向二维数组的双指针。您只需要使用其中之一 - 双指针 二维数组,如下所示:

int mat[N][N];

然而,更大的问题来自于尝试交换二维数组和双指针。这 isn't possible in C 因为二维数组在内存中是平放的。 您需要自己创建一个指针数组 mat_ptr,然后将其传递给 casual_generationprompt_print.

最后,这些 casual_generationprompt_print 函数需要一个指针,所以你不应该在调用函数之前用 ** 取消引用指针。

最终的工作代码是:

int main()
{
    int mat[N][N];
    int *mat_ptr[N];
    for (int i = 0; i < N; i++)
        mat_ptr[i] = mat[i];

    casual_generation(mat_ptr);
    prompt_print(mat_ptr);
}

您可以在另一个答案中找到为什么代码崩溃的详细解释,我只会提出一个非常优雅的解决方案,它使用 C99 中一个简洁但鲜为人知的功能,称为 可变长度数组 (又名 VLA)。

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

void casual_generation(int n, int mat[n][n]);
void prompt_print(int n, int mat[n][n]);

int main()
{
    const int N = 5;
    int mat[N][N];
    casual_generation(N, mat);
    prompt_print(N, mat);
}

void casual_generation(int n, int mat[n][n])
{
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            mat[i][j] = rand() % 50;
}

void prompt_print(int n, int mat[n][n])
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
            printf("%d  ", mat[i][j]);
        printf("\n");
    }
}

它以迂腐的模式编译,没有任何警告,而且工作起来很有魅力。 VLA 被引入到 C 中以简化多维数组的数值计算。