【问题标题】:What's the difference between int[ ][ ] and int** (noobie)int[ ][ ] 和 int** 有什么区别(noobie)
【发布时间】:2017-03-26 11:20:26
【问题描述】:

例如,我想将一个二维整数数组传递给 C 中的一个函数。该函数是否应该定义为

void foo(int**,int m,int n);

void foo(int[][],int m,int n);

我在第一个原型声明中收到错误,但在使用 GCC 编译时收到警告“指针类型不兼容”。请解释如何传递二维数组。主要方法如给定

int main()  
{  
    int m,n;  
    scanf("%d%d",&m,&n); 
    int a[m][n];  
    foo(a,m,n);  
    return 0;  
}  

【问题讨论】:

  • 指针不是数组,数组也不是指针。阅读此stackoverflow.com/questions/42094465/…
  • 使用常见的布尔逻辑对“函数应该定义为 X 还是 Y”的问题的答案是“否”。二维数组都不正确。
  • ... 在你的情况下什么是正确的很大程度上取决于你用什么来调用这个函数。
  • 第一个是合法的C;第二个是不是。你 传递 给前者(我怀疑你从中得到警告的那个)很重要,并且不方便地包含在你的问题中。传递 real 二维数组(数组的数组)需要所有维度,但在形成函数的形式参数时需要知道最高级的维度,无论是通过常量还是通过可变长度数组 (VLA)语义。
  • @AdvaithHL 不,它不是那样工作的。您是需要首先在此处提供代码的人。您说您有代码并且发出警告或编译时错误,那么您有责任提供MCVE

标签: c arrays


【解决方案1】:

两个函数声明

void foo(int**,int m,int n);

void foo(int[][],int m,int n);

错了。具有数组类型的参数的声明被调整为指向元素类型的指针。因此,如果您声明了具有像这样的二维数组类型的参数

int a[M][N]

然后调整为

int ( *p )[N}

函数定义必须知道 N 的值。如果N 是声明的编译时常量,例如

#define N 10

那么函数声明可以是这样的

void foo( int[][N], int );

void foo( int[][N], size_t );

其中第二个参数指定“行”的数量。 “列”的数量是已知的,因为正如我所说,它是一个编译时常量。

如果 N 是在运行时计算的并且编译器支持变长数组,那么你应该像这样声明函数

void foo( int, int, int[*][*] );

或者使用size_t 类型来代替int 作为尺寸类型更好。例如

void foo( size_t, size_t, int[*][*] );

请注意,至少第二个参数必须在数组的声明之前,因为它用于确定数组的元素类型(其大小应为函数定义所知)。

对于不是其定义的函数声明,参数名称不是必需的。

这里展示了如何使用可变长度数组表示法声明和定义函数。

#include <stdio.h>

void foo( size_t, size_t, int[*][*] );

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

#define M   2
#define N   3

int main(void) 
{
    int a[M][N] =
    {
        { 1, 2, 3 },
        { 4, 5, 6 }
    };

    foo( M, N, a );

    size_t m = 3;
    size_t n = 2;

    int b[m][n];

    for ( size_t i = 0; i < M; i++ )
    {
        for ( size_t j = 0; j < N; j++ ) b[j][i] = a[i][j];
    }

    putchar( '\n' );

    foo( m, n, b );

    return 0;
}

程序输出是

1 2 3 
4 5 6 

1 4 
2 5 
3 6 

第三个参数也可以这样声明

void foo( size_t m, size_t n, int a[][n] )

void foo( size_t m, size_t n, int ( *a )[n] )

因为如前所述,数组已调整为指向其元素类型的指针。

【讨论】:

    【解决方案2】:

    您问题中的任何一种形式都不正确;后者,[][] 在语法上也是非法的。

    您正在使用称为可变长度数组 (VLA) 的 C99 和 C11 功能。要正确声明接受 VLA 的函数,您需要先提供维度,然后提供实际数组,并在参数中提供维度的参数名称:

    void foo(int m, int n, int array[m][n]);
    

    (在 declarations 中但不在 definitions 中,也可以使用 [*] 指定未指定的维度,但我认为它没有多大意义这使得它明确哪个参数对应于哪个维度)。

    请注意,提供维度的参数(mn)必须出现在参数列表中的数组之前

    【讨论】:

      【解决方案3】:

      在声明变量时,了解内存的布局方式很重要。

      线性/基于堆栈的数组

      以下声明了一个 2D 数组,在 'stack' 上有 5x5 个元素:

      void foo(void) {
          int a[5][5];
      
          ...
      }
      

      内存是这样的:

      如果您要将这个数组传递给一个函数,那么您将传递第一个元素的地址a[0][0](如紫色箭头所示)。 当接收函数希望访问第二个维度时,它需要知道'步幅',这是您在void foo(int a[][]) 的原型中没有提供的——因此会出现错误。

      指针/基于堆的数组

      以下将在“”上声明相同的 5x5 2D 数组(可以在堆栈上执行所有操作):

      void foo(void) {
          int **a;
          int *a_data;
          int i;
      
          a_data = malloc(sizeof(*a_data) * (5 * 5);
          a = malloc(sizeof(*a) * 5);
      
          for (i = 0; i < 5; i++) {
              a[i] = &(a_data[i * 5]);
          }
      
          ...
      
          free(a);
          free(a_data);
      }
      

      内存是这样的:

      将 this 传递给函数时,传递的是“第一维”的地址。由于这里的内存结构不同,我们可以很容易地查找维度开始的地址,'stride'现在无关紧要了。

      【讨论】:

      • 后一个不是正确的5x5数组;也可以使用 malloc 分配适当的多维数组。甚至是 VLA!
      • 当然是!内存中还有25个元素,它们是通过二维/索引来访问的……只是你需要一个支持的数组/表才能正确访问它。
      • 可以这样做int (*a_data)[5]; a_data = malloc(5 * sizeof *a_data); 来获得一个正确的连续数组。
      • 有趣,谢谢...虽然我发现这有点神秘/难以阅读...您需要确定自己的优先级才能理解它。但是,是的,它实现了同样的目标。
      猜你喜欢
      • 1970-01-01
      • 2020-05-05
      • 2015-11-04
      • 1970-01-01
      • 2014-10-20
      • 2017-02-23
      • 1970-01-01
      • 2021-04-02
      • 2018-08-26
      相关资源
      最近更新 更多