【问题标题】:What is the best way to make 2 dimensional array in C在C中制作二维数组的最佳方法是什么
【发布时间】:2024-01-09 06:23:02
【问题描述】:

我想在 C 中创建一个二维数组。

我知道一种方法可以做到这一点。

#include <stdlib.h>

void    my_func(int **arr)
{
        printf("test2: %d\n", arr[0][1]);
}

int     main(void)
{
        const int row = 3;
        const int col = 4;

        int **arr = (int **)malloc(sizeof(int *) * 3);
        arr[0] = (int *)malloc(sizeof(int) * 4);
        arr[1] = (int *)malloc(sizeof(int) * 4);
        arr[2] = (int *)malloc(sizeof(int) * 4);

        arr[0][0] = 1;
        arr[0][1] = 2;
        arr[0][2] = 3;
        arr[0][3] = 4;
        arr[1][0] = 3;
        arr[1][1] = 4;
        arr[1][2] = 5;
        arr[1][3] = 6;
        arr[2][0] = 5;
        arr[2][1] = 6;
        arr[2][2] = 7;
        arr[2][3] = 8;

        printf("test1: %d\n", arr[0][1]);

        my_func(arr);

}

在这种情况下,数组可以作为参数传递给函数。 但它不是那么漂亮。 如果数组有很多值(例如 20*20),我需要逐行键入每个值。

于是我搜索了一下,找到了一种制作这样一个数组的方法。

#include <stdio.h>

void    my_func(int **arr)
{
        printf("test2: %d", arr[0][1]);
}

int     main(void)
{
        const int row = 3;
        const int col = 4;

        int arr[row][col] = {
                {1,2,3,4},
                {3,4,5,6},
                {5,6,7,8}
        };
        printf("test1: %d", arr[0][1]);

        my_func(arr);
}

它很简洁,不会让我筋疲力尽。 但是当数组传递给函数时出现了问题。 并且在编译的时候会出现如下警告

test_2D_array.c:20:11: warning: incompatible pointer types passing 'int [3][4]' to
      parameter of type 'int **' [-Wincompatible-pointer-types]
                my_func(arr);
                        ^~~
test_2D_array.c:3:20: note: passing argument to parameter 'arr' here
void    my_func(int **arr)
                      ^
1 warning generated.

甚至函数也无法访问数组参数。存在分段错误。

所以我想知道制作数组的最佳方法,该数组可以作为参数传递给任何函数,并且比我的第一个代码更不累。

感谢您的阅读。

【问题讨论】:

  • 在你的情况下:arr 不是int **;它实际上是一个int [][4]int *[4]
  • 您的第一个示例 不是 一个二维数组,因为 C 执行它[您的第二个示例 一个二维数组]。您的第一个是指向 [separate] 一维数组的 指针 数组。这就是具有二维数组的语言所做的(例如perlpython)。您的第二个:将功能更改为:void my_func(int row,int col,int arr[row][col]) { ... } 并调用 [来自main] 与:my_func(row,col,arr);
  • 您确定int arr[row][col] = { {1,2,3,4},...}; 有效吗?我收到error: variable-sized object may not be initialized
  • 将数组传递给函数时出现问题 那是因为int **arr = (int **)malloc(sizeof(int *) * 3); 然后arr[0] = (int *)malloc(sizeof(int) * 4); ... 不创建二维数组 .不好意思喊了,但是“多个malloc()二维数组”实际上是一个指向多个完全独立的一维数组的指针的一维数组。它实际上不是一个数组。见Correctly allocating multi-dimensional arrays
  • @AndrewHenle 您大喊的问题是可以使用arr[y][x] 语法访问该数组。所以它表现得“好像”它一个二维数组。换句话说,它确实创建了一个二维数组,而不是您对二维数组的狭义定义。

标签: c multidimensional-array declaration definition variable-length-array


【解决方案1】:

这个

int **arr = (int **)malloc(sizeof(int *) * 3);

不是二维数组的声明或分配

这里创建了一个元素类型为int * 的一维数组。然后一维数组的每个元素依次指向一个分配的一维数组,元素类型为int

这个二维数组的声明

    const int row = 3;
    const int col = 4;

    int arr[row][col] = {
            {1,2,3,4},
            {3,4,5,6},
            {5,6,7,8}
    };

不正确。可变长度数组(并且您声明了一个可变长度数组)可能不会在声明中初始化。

你可以改写

    enum { row = 3, col = 4 };

    int arr[row][col] = {
            {1,2,3,4},
            {3,4,5,6},
            {5,6,7,8}
    };

当这样一个数组被传递给一个函数时,它被隐式转换为指向它的第一个int ( * )[col]类型元素的指针。

您可以通过以下方式将其传递给具有可变长度数组类型参数的函数

void    my_func( size_t row, size_t col, int arr[row][col] )
{
        printf("test2: %d", arr[0][1]);
}

或者如果将枚举的定义放在函数声明之前

    enum { row = 3, col = 4 };

那么函数也可以声明为

void    my_func( int arr[][col], size_t row )
{
        printf("test2: %d", arr[0][1]);
}

这是一个演示程序,展示了三种不同的方法。第一个是使用数组大小​​的编译时常量定义数组时。第二个是创建可变长度数组时。第三个是动态分配指向一维数组的指针的一维数组。

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

enum { row = 3, col = 4 };

void output1( int a[][col], size_t row )
{
    for ( size_t i = 0; i < row; i++ )
    {
        for ( size_t j = 0; j < col; j++ )
        {
            printf( "%d ", a[i][j] );
        }
        putchar( '\n' );
    }
}

void output2( size_t row, size_t col, int a[row][col] )
{
    for ( size_t i = 0; i < row; i++ )
    {
        for ( size_t j = 0; j < col; j++ )
        {
            printf( "%d ", a[i][j] );
        }
        putchar( '\n' );
    }
}

void output3( int **a, size_t row, size_t col )
{
    for ( size_t i = 0; i < row; i++ )
    {
        for ( size_t j = 0; j < col; j++ )
        {
            printf( "%d ", a[i][j] );
        }
        putchar( '\n' );
    }
}


int     main(void)
{
        int arr1[row][col] = 
        {
                {1,2,3,4},
                {3,4,5,6},
                {5,6,7,8}
        };

        output1( arr1, row );
        putchar( '\n' );

        const size_t row = 3, col = 4;

        int arr2[row][col];

        memcpy( arr2, arr1, row * col * sizeof( int ) );

        output2( row, col, arr2 );
        putchar( '\n' );

        int **arr3 = malloc( row * sizeof( int * ) );

        for ( size_t i = 0; i < row; i++ )
        {
            arr3[i] = malloc( col * sizeof( int ) );
            memcpy( arr3[i], arr1[i], col * sizeof( int ) );
        }

        output3( arr3, row, col );
        putchar( '\n' );

        for ( size_t i = 0; i < row; i++ )
        {
            free( arr3[i] );
        }

        free( arr3 );
} 

程序输出是

1 2 3 4 
3 4 5 6 
5 6 7 8 

1 2 3 4 
3 4 5 6 
5 6 7 8 

1 2 3 4 
3 4 5 6 
5 6 7 8 

注意函数output2可以与数组arr1一起使用,就像它与数组arr2一起使用一样。

【讨论】:

  • 感谢您的好意。很有帮助。
【解决方案2】:

函数可以声明为

void my_func(int arr[][4])
{
    printf("test2: %d", arr[0][1]);
}

请注意,您不必指定第一个维度的大小。

【讨论】:

  • 你的意思是...你不必指定第一个维度的大小...而不是...你不能... ?
【解决方案3】:

假设没有动态分配。

1   #include <stdio.h>
  1
  2 void func(int *arr, int row, int col) {
  3     int i, j;
  4
  5     for (i = 0; i < row * col; i++) {
  6         if (i && (i % col == 0))
  7             printf("\n");
  8         printf("%d ", arr[i]);
  9     }
 10
 11     printf("\n");
 12 }
 13
 14 int main(int argc, char *argv[]) {
 15     // can be this
 16     int arr1[] = {
 17         1,2,3,  // row 0
 18         4,5,6   // row 1
 19     };
 20
 21     // or this way
 22     int arr2[2][3] = {
 23         {0,1,2},  // row 0
 24         {4,5,6}   // row 1
 25     };
 26
 27     func(arr1, 2, 3);
 28     func((int*)arr2, 2, 3);
 29     return 0;
 30 }
~

【讨论】:

    【解决方案4】:

    首先,您无法初始化具有可变大小的二维数组,正如他在回答中提到的“来自莫斯科的弗拉德”。 相反,您只需指定第二维的大小,将第一维留空。 其次,您的 my_func(int **arr) 期待 pointer to pointer to int 而您只是传递了数组的地址,这就是编译器抛出不兼容错误的原因。

    您的固定代码将如下所示:

    #include <stdio.h>
    
    void    my_func(int **arr)
    {
            printf("test2: %d", arr[0][1]);
    }
    
    int     main(void)
    {
            int arr[][4] = {1,2,3,4,
                            3,4,5,6,
                            5,6,7,8};
            int *p = (int *)arr;
            int **p1 = &p;
            printf("test1: %d", arr[0][1]);
    
            my_func(p1);
    }
    

    现在const int row = 3const int column = 4 已经没有用了,所以你可以删除它们。

    【讨论】:

    • 你为什么在你的第一个代码中使用#include&lt;stdlib.h&gt;,顺便说一句?
    • 用于malloc。我也应该写&lt;stdio.h&gt;。但是我在复制和粘贴时忘记了它。谢谢你的回答。
    • pointer to pointer to int这个表达式对我理解真的很有帮助。太感谢了。我能问个问题吗?我如何阅读或理解int [][4]
    • 我想,您是在问 我如何阅读或理解 int arr[ ][4] 声明二维数组采用以下一般形式:type varName[size][size] 当数组完全初始化时所有值,明确地,我们不需要指定第一个维度的大小。在我们的示例中,我们声明并初始化了一个数组int arr[ ][4],这意味着我们知道行和列的大小。因此,我们可以将其读取为 3x4 元素的数组。二维数组就像数学中的矩阵。
    • 如果你对我们为什么要指定二维的大小感到好奇,那是因为编译器会从所有值中分离出 4 个元素并将它们存储在一行中。所以,它会首先将1,2,3,4 分开并将它们存储在一行中,然后是接下来的 4 个元素,依此类推。
    【解决方案5】:

    它需要一个看起来很有趣的类型转换,但我是这样做的:

    #include <stdio.h>
    
    void my_func(int **arr, int cols)
    {
        int (*matrix)[cols] = arr;
        printf("test2: %d\n", matrix[0][1]);
    }
    
    int main(void)
    {
            const int row = 3;
            const int col = 4;
    
            int arr[3][4] = {
                    {1,2,3,4},
                    {3,4,5,6},
                    {5,6,7,8}
            };
            printf("test1: %d\n", arr[0][1]);
    
            my_func(arr, col);
    }
    

    IDEOne Link

    【讨论】:

    • 警告:不兼容的指针类型将int [3][4] 传递给int * 类型的参数您最好将int *arr 声明为void *arr。那么你甚至不需要my_func中的演员表。
    • 警告:不兼容的指针类型将int [3][4] 传递给int ** 类型的参数,并且警告:使用int ** 类型的表达式初始化int (*)[cols] 的指针类型不兼容。编辑使问题变得更糟。您要么需要正确输入类型,要么使用 void* 绕过类型检查。
    最近更新 更多