使用变量初始化数组

时间:2013-06-19 01:32:37

标签: c multidimensional-array

我正在尝试将二维数组传递给函数。 我没有把它传递给函数的麻烦。但是我无法理解这背后的逻辑。函数和主要定义是这样的:

// 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. 是否可以使用指向指针的方式遍历二维数组?
  4. 有人能建议我对这个主题有一个很好的解读,这可以清除我的所有概念吗?

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

6 个答案:

答案 0 :(得分:7)

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

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

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

    init_2d(a, m, n);
    

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

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

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

    这里真正可以做的是将2D数组衰减到指向 1D数组的指针。信不信由你,这正是你在print声明中所做的,即使它没有立即引起注意。您的

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

    声明实际上等同于

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

    声明。即a实际上是指向int [y]类型的指针 - 指向大小为int的1D y数组的指针。上述两个声明只是表达同一事物的两种表面上不同的方式。

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

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

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

    print(m, n, &a);
    

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

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

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

答案 1 :(得分:4)

  1. 在某些版本的C中存在VLA(可变长度数组).VLA允许使用运行时分配的变量在堆栈上分配固定长度的数组,而不是通过malloc()或类似函数在堆上分配

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

答案 2 :(得分:4)

  1. ISO C90禁止使用可变长度数组,但您的编译器更新,所以没关系。如果使用-ansi -pedantic -Wall flags
  2. 进行编译,则会失败
  3. 如果使用-ansi -pedantic -Wall标志进行编译(ISO C90也不喜欢它),这也将失败。你的新编译器很好用。
  4. 是的,您可以使用指向遍历2d数组的指针(实际上,main的char ** argv参数就是一个例子)
  5. 您正在阅读的图书看起来符合C90标准。可以使用较新版本的gcc。

答案 3 :(得分:3)

  1. VLA。
  2. init_2d需要int *,但您已将其传递给int (*)[n]。但是,由于a是数组的数组,因此a[0]的地址与a[0][0]的地址相同,这就是init_2d“工作”的原因。当您将a传递给init_2d时,您的编译器应警告您类型不兼容。如果没有,您应该向您的编译器供应商抱怨。

    print使用VLA作为第三个参数。

  3. 如果您的意思是“我可以使用a遍历int **,答案是”不是直截了当“。原因是如果您有int **p,如果a初始化为{:1}},则只能逼真地遍历p

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

    但是,您只需通过解除引用a通过ap遍历p。指向指针的指针通常用于表示指针数组。

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

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

答案 4 :(得分:1)

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

a int**(2-dim:N x M)也可以看作是int*一维数组(大小:N * M)... < / p>

请参阅1. 2.和3。

的其他答案

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

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

答案 5 :(得分:0)

1)你的main中的m和n已经有了一个值,所以你的数组声明就好了。它们只是用作数字常量。 2)数组是指针。所以

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

完全相同。

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

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