int ** vs int [const] [const]差异

时间:2014-04-25 21:46:23

标签: c multidimensional-array

前几天我正在编写代码,我发现它很奇怪,int **和int [] []的行为方式不同。谁能指出它们之间的差异?下面是我的示例代码,如果我传递的是常量大小的2d数组,它会因为分段错误而失败,而当我传递一个dinamically分配的2d数组时,它确实正常工作。

我很困惑主要是因为ant int []数组的工作方式与int *相同。

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

void sort_by_first_row(int **t, int n, int m)
{
    int i, j;


    for(i = m-1 ; i > 0 ; --i)
    {
        for(j = 0 ; j < i; ++j)
        {

            if(t[0][j] < t[0][j+1])
            {
                int k;  
                for(k = 0 ; k < n ;++k)
                {
                    int swap;
                    swap = t[k][j];
                    t[k][j] = t[k][j+1];
                    t[k][j+1] = swap;
                }
            }
        }
    }
}

int main(void) {
    int i, j;
    /* Working version */   
    /*int **t;

    t =(int**) malloc(3*sizeof(int*));
    for(i = 0; i  < 3; ++i)
    {
        t[i] = (int*) malloc(6*sizeof(int));
    }*/

    /*WRONG*/
    int t[3][6];

    t[0][0] = 121;
    t[0][1] = 85;
    t[0][2] = 54;
    t[0][3] = 89;
    t[0][4] = 879;
    t[0][5] = 11;

    for( i = 0; i < 6; ++i )
        t[1][i] = i+1;

    t[2][0] = 2;
    t[2][1] = 4;
    t[2][2] = 5;
    t[2][3] = 3;
    t[2][4] = 1;    
    t[2][5] = 6;                

    sort_by_first_row(t, 3, 6);

    for(i = 0; i < 3; ++i)
    {
        for(j = 0; j < 6; ++j)
            printf("%d ", t[i][j]);
        printf("\n");
    }
    return 0;
}

因此,基于以下答案,我意识到,多维数组以连续主要顺序连续存储。经过一些修改后,下面的代码可以工作:

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

void sort_by_first_row(int *t, int n, int m)
{
    int i, j;


    for(i = m-1 ; i > 0 ; --i)
    {
        for(j = 0 ; j < i; ++j)
        {

            if(t[j] < t[j+1])
            {
                int k;  
                for(k = 0 ; k < n ;++k)
                {
                    int swap;
                    swap = t[k*m + j];
                    t[k*m + j] = t[k*m + j+1];
                    t[k*m + j+1] = swap;
                }
            }
        }
    }
}

int main(void) {
    int i, j;
    /* Working version */   
    /*int **t;

    t =(int**) malloc(3*sizeof(int*));
    for(i = 0; i  < 3; ++i)
    {
        t[i] = (int*) malloc(6*sizeof(int));
    }*/

    /*WRONG*/
    int t[3][6];

    t[0][0] = 121;
    t[0][1] = 85;
    t[0][2] = 54;
    t[0][3] = 89;
    t[0][4] = 879;
    t[0][5] = 11;

    for( i = 0; i < 6; ++i )
        t[1][i] = i+1;

    t[2][0] = 2;
    t[2][1] = 4;
    t[2][2] = 5;
    t[2][3] = 3;
    t[2][4] = 1;    
    t[2][5] = 6;                

    sort_by_first_row(t, 3, 6);

    for(i = 0; i < 3; ++i)
    {
        for(j = 0; j < 6; ++j)
            printf("%d ", t[i][j]);
        printf("\n");
    }
    return 0;
}

我的新问题是:如何修改代码,以便程序适用于int [] []和int **

4 个答案:

答案 0 :(得分:3)

int **与int [] []完全不同。 int **只是指向指针的指针,如下所示:

enter image description here

实际上,您可以使用简单的int *指向第一个元素来访问整个多维数组,并从中进行简单的数学运算。

以下是单独分配的结果(在您的注释代码中):

enter image description here

但是,当您分配多维数组时,所有内存都是连续的,因此很容易进行简单的数学运算以达到所需的元素。

int t[3][6];
int *t = (int*) malloc((3 * 6) * sizeof(int));  // <-- similarly

这将导致所有元素的连续内存块。

您当然可以使用单独的分配,但是您需要以不同的方式使用内存。

希望这会有所帮助。

答案 1 :(得分:3)

意识到int **t使t指向指针,而int t[3][6]使t成为数组的数组。在大多数情况下,当在表达式中使用数组时,它将成为其第一个成员的地址值。因此,对于int t[3][6],当t传递给函数时,该函数实际上将获得&t[0]的值,该值具有指向数组的类型指针(在本例中为{{ 1}})。

指向的类型对索引时指针的行为方式很重要。当指向对象的指针增加5时,它指向当前对象后面的第5个对象。因此,对于int (*)[6]int **t将指向第5个指针,而对于t + 5,t + 5将指向第5个数组。也就是说,int (*t)[M]的结果与t + 5的结果相同。

在您的情况下,您已实现&t[5],但您传递的是不兼容的指针。也就是说,void sort_by_first_row(int **t, int n, int m)的类型(&t[0]中将t将变为main)与函数所需的类型int **t不同。因此,当排序函数开始使用该地址时,当底层结构是一个数组数组时,它会认为它的索引是指针。

答案 2 :(得分:1)

int **是指向int指针的指针,而可以是指向int数组指针数组的指针。 int [][]int s的二维数组。在一个方面,二维数组与C中的一维数组完全相同:它基本上是指向第一个对象的指针。唯一的区别是访问,同时访问两个不同步幅的二维数组 长话短说,int[][]更接近int*而不是int**

答案 3 :(得分:1)

int t[3][6]int t[18]几乎完全相同。在两种情况下都分配了一个由18个整数组成的连续块。变量t提供了这个整数块的起始地址,就像一维情况一样。

将此与您标记为“工作”的情况进行对比,其中t为您提供3个指针块的地址,每个指针指向一个包含6个整数的内存块。这是一种完全不同的动物。

t[3][6]t[18]之间的区别在于编译器会记住数组每个维度的大小,并自动将2D索引转换为1D偏移量。例如,编译器会自动将t[1][2]转换为*(t + 1*6 + 2)(如果它被声明为一维数组,则相当于t[8]。)

将多维数组传递给函数时,有两种方法可以处理它。第一种是将函数参数声明为具有已知尺寸大小的数组。第二种方法是将数组视为一维数组。

如果您要声明数组的大小,您可以声明您的函数:

void sort_by_first_row(int t[][6], int n)

或者

void sort_by_first_row(int t[3][6])

您必须声明所有数组维度大小,或者您可以省略第一个大小。在这两种情况下,您都可以使用t访问t[i][j]的元素;您已经为编译器提供了足够的信息来执行从2D表示法转换为1D索引偏移的偏移量数学。

如果将其视为一维数组,则必须传递数组维度,然后自行进行偏移数学运算。

以下是一个完整的工作示例,其中ff2都生成相同的输出:

void f(int* t, int m, int n)
{
for (int i = 0; i < m; i++)
    for (int j = 0; j < n; j++)
        std::cout << t[i * n + j] << " ";
std::cout << std::endl;
}

void f2(int t[][6], int m)
{
for (int i = 0; i < m; i++)
    for (int j = 0; j < 6; j++)
        std::cout << t[i][j] << " ";
std::cout << std::endl;
}

int main()
{
int t[3][6];
int val = 1;
for (int i = 0; i < 3; i++)
{
    for (int j = 0; j < 6; j++)
    {
        t[i][j] = val;
        val++;
    }
}

f(&(t[0][0]), 3, 6);
f2(t, 3);

return 0;
}

有一点需要注意的是我必须将t传递给f的黑客方式。我用C / C ++编写了一段时间,但我记得能够直接传递t。也许有人可以告诉我为什么我现在的编译器不会让我。