前几天我正在编写代码,我发现它很奇怪,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 ** ?
答案 0 :(得分:3)
int **与int [] []完全不同。 int **只是指向指针的指针,如下所示:
实际上,您可以使用简单的int *指向第一个元素来访问整个多维数组,并从中进行简单的数学运算。
以下是单独分配的结果(在您的注释代码中):
但是,当您分配多维数组时,所有内存都是连续的,因此很容易进行简单的数学运算以达到所需的元素。
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索引偏移的偏移量数学。
如果将其视为一维数组,则必须传递数组维度,然后自行进行偏移数学运算。
以下是一个完整的工作示例,其中f
和f2
都生成相同的输出:
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
。也许有人可以告诉我为什么我现在的编译器不会让我。