3

我正在尝试将二维数组传递给函数。我没有麻烦将它传递给函数。但我很难理解这背后的逻辑。功能和主要定义如下:

// Function to print the two-dimensional array
void print(int x, int y, int a[x][y]){
    printf("\n");
    int i, j;
    for(i = 0; i < x; i++){
        for(j = 0; j < y; j++)
            printf("%d     ", a[i][j]);
        printf("\n");
    }
}

// Function to initialize the two-dimensional array
void init_2d(int *a, int x, int y){
    int i, j;
    for(i = 0; i < x; i++){
        for(j = 0; j < y; j++){
            a[i*y + j] = i + j;
        }
        printf("\n");
    }
}

int main(){
    int m = 2, n = 3;
    int a[m][n];  // a two dimensional whose size has been defined using m and n
    init_2d(a, m, n);
    print(m, n, a);
}

具有讽刺意味的是,一切都运行良好。那是我的问题,因为我无法消化它的逻辑。

主要问题是:

  1. 我在书中读到的是,二维数组的大小应该使用常量或符号常量来定义。在我的主要工作中,我正在使用变量定义二维数组mn但它工作正常。为什么?
  2. 我还被告知通过将二维数组衰减为一维数组(通过在函数中将其定义为指向 int 的指针)来传递二维数组,即我在函数中所做的方式init_2d。但在print函数中,我使用的是二维数组,其大小已使用变量xy. 这样做可以吗?
  3. 是否也可以使用指向指针的指针来遍历二维数组?

任何人都可以建议我好好读一下这个主题,它可以清除我所有的概念吗?

我正在使用代码块来编译我的代码,编译器是GNU GCC Compiler

4

6 回答 6

7
  1. 是和不是。您在书中读到的内容适用于 C 语言规范的较旧的原始版本 - C89/90。由于 C99 版本的 C 语言编译器支持所谓的可变长度数组 (VLA),其大小可以由运行时值指定。您无意中使用了该语言的 C99 特定功能,您的编译器在其默认模式下显然支持该功能。如果您要求编译器切换到严格的 C89/90 模式,您的代码将无法编译,因为您使用了非常量来指定数组大小。

    请注意,即使在 C99 中,VLA 也仅在某些上下文中受支持,例如局部数组和函数参数声明。您将无法声明静态 VLA 或作为结构类型成员的 VLA。在这些情况下,即使在 C99 中,您仍然需要使用常量数组大小。

  2. 谁告诉你将二维数组衰减为一维数组是非常错误的。您在程序中使用的方式init_2d无效:不允许您自己将二维数组“重新解释”为一维数组。任何 C 编译器都会立即抱怨你的

    init_2d(a, m, n);
    

    调用,因为此调用尝试为参数传递二维int数组int *参数。这是非法的。要使编译器静音,您必须这样做

    init_2d((int *) a, m, n);
    

    您很有可能init_2d会“按预期工作”,但仍然没有充分的理由使用这样的黑客。

    这里真正可以做的是将二维数组衰减为指向一维数组的指针。信不信由你,这正是您在print声明中所做的,即使它不会立即引起注意。您的

    void print(int x, int y, int a[x][y])
    

    声明实际上等价于

    void print(int x, int y, int (*a)[y])
    

    宣言。即a实际上是一个指向类型int [y]的指针——一个指向大小为一维int数组的指针y。以上两个声明只是说同一件事的两种表面上不同的方式。

    另请注意,不需要通过将二维数组“衰减”到任何东西来将它们传递给函数。您可以通过指向整个 2D 数组的指针传递 2D 数组,如

    void print(int x, int y, int (*a)[x][y])
    

    在这种情况下,要传递数组,您必须使用&运算符

    print(m, n, &a);
    

    并且要访问函数内部的数组,您必须使用*运算符

    printf("%d     ", (*a)[i][j]);
    
  3. 不,在你的情况下它不可能。指针对指针无法遍历内置二维数组。顺便说一句,正如您在init_2d实现中已经发现的那样(“有效”,即使它是非法的),2D 数组在内部实现为具有自动 2D 到 1D 索引重新计算的 1D 数组。指针对指针不会帮助您遍历这种“平坦”的线性数据。

    在处理完全不同类型的多维数组时,通常使用指针对指针类型(或更一般地,多级指针类型):当 N 维数组实现为指向独立分配的指针数组时(N-1) 维数组。这种多维数组在 C 程序中也经常使用,但必须手动实现和管理(您可以在 SO 上找到很多示例)。同时,正如我上面所说,内置语言数组以完全不同的方式实现,它们不能被多级指针遍历。

于 2013-06-19T03:46:25.757 回答
4
  1. VLA(可变长度数组)存在于某些版本的 C 中。VLA 允许使用运行时分配的变量在堆栈上分配固定长度的数组,而不是在堆上通过malloc()或类似的函数。

  2. 我以前从未见过使用过的数组声明语法print(),所以我无法回答。通常,人们会将括号留空,或者使用衰减的指针。

  3. 是的。

于 2013-06-19T01:39:16.640 回答
4
  1. ISO C90 禁止变长数组,但是你的编译器比较新,所以没关系。如果您使用 -ansi -pedantic -Wall 标志进行编译,它将失败
  2. 如果您使用 -ansi -pedantic -Wall 标志编译(ISO C90 也不喜欢它),这也会失败。不过,您的较新编译器可以使用它。
  3. 是的,可以用指针来遍历二维数组(其实char** argv参数formain就是一个例子)

您正在阅读的书籍看起来符合 C90 标准。不过,较新版本的 gcc 还可以。

于 2013-06-19T01:41:21.383 回答
3
  1. VLA。
  2. init_2d需要 a int *,但你已经通过了 a int (*)[n]。但是,由于a是数组的数组,因此 的地址与a[0]的地址相同a[0][0],这就是init_2d“有效”的原因。当您传递ainit_2d. 如果没有,您应该向您的编译器供应商投诉。

    print第三个参数使用 VLA。

  3. 如果你的意思是“我可以a用遍历int **,答案是“不是以一种直接的方式”。原因是如果你有int **p,你只能在像这样初始化的a情况下实际遍历:p

    int a[n][m];
    int *ap = &a[0][0];
    int **p = &ap;
    

    但是,那么您只是通过取消引用遍历avia 。指向指针的指针通常用于表示指针数组。app

    int *x[10];
    int **p = x;
    

    但是,你a不是一个指针数组。

于 2013-06-19T02:46:07.697 回答
1

C 数组中的内存分配是连续的,这就是表达式a[i][j](init_2d) 可以同化为a[i*y + j](print) 的原因。

a因为int**(2-dim : N x M) 也可以看作是一个int*一维数组 (size : N*M) ...

请参阅其他人对 1. 2. 和 3 的好答案。

请参阅:Row-major order (C-style),所以 2. 很好。

注意: 显然,我在这里所说的仅适用于最新版本的 C 编程语言和/或某些特定上下文,并且绝对不可推广。所以最好先阅读其他几个答案。

于 2013-06-19T01:45:36.923 回答
0

1)您 main 中的 m 和 n 已经有一个值,因此您的数组声明很好。它们仅用作数字常量。2)数组是一个指针。所以

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

完全一样。

数组 int a[2][2] 是一个一维数组,有 2 个指向 int 的指针,而这 2 个指针指向 2 个整数。因此,在您的第一个指针周围正确播放(在您的情况下为 a),您可以访问这些整数。

3)查看维基百科。有一个很好的算法可以更好地理解。

于 2013-06-19T01:43:57.170 回答