假设我有一个二维数组grid
声明为double grid[5][5]
。据我所知,以下说法是正确的:
- 当声明
grid
时,一个连续的内存块被分配为5*5倍,不多也不少 - 当用符号
grid[i][j]
访问数组的元素时,这段代码实际上被解释为*(grid+(i*5+j))
另一方面,我知道我也可以通过编写以下内容来存储与指针数组相同的矩阵:
double ** grid;
grid = (double**)malloc(sizeof(double*)*5);
for (i=0; i<5; i++)
grid[i] = (double*)malloc(sizeof(double)*5);
实际上我有一个代码可以做到这一点。问题是,它会像以前一样,使用双下标表示法访问grid
的元素。这种情况不同吗?在这种情况下,grid[i][j]
是否转换为*(*(grid+i)+j)
,即转换为双重解引用?这是我唯一能看到它正确发生的方式。
(这个问题可能源于我(缺乏(对C中指针和数组类型之间关系的理解。(
编辑:
好的,让我们看看我是否明白了:
- 9总是被转换成CCD_ 10
- 在这两种情况下,这个表达式的计算方式确实不同,因为正如Jim在回答中所说,指针算术考虑了所指向类型的大小;然而,在这两种情况下都提取了正确的元素
- 如果且仅当"grid"是2D数组,则此表达式被进一步优化为
*( (double*)grid + (i*5+j) )
,这是可能的,因为编译器知道任何grid[i]
实际上都是从位置grid+i*5
开始的数组
但这给我留下了一个不可避免的结论:对于2D阵列,如果我设置了i=j=0
,那么我就有了**grid == *((double*)grid)
。这是正确的吗?
当使用表示格[i][j]访问数组的元素时,这段代码实际上被解释为
*(grid+(i*5+j))
。
没有。(不知道为什么这么多答案都是肯定的(。*(grid+(i*5+j))
与grid[i*5+j]
相同,是对i
和j
的某些值的越界访问。此外,这指定了一个数组,而不是内部
以下两个表达式在所有情况下都是完全等价的:A[i][j]
*(*(grid+i)+j)
。
通过在两种不同的去引用符号之间进行转换,您永远不会"获得"任何东西。它只是指定特定对象的左值表达式的两种等效语法形式。
在剩下的回答中,我将使用[ ]
语法,因为我发现它更清晰。
也许你想问这样的问题:"对于int A[5][5];
,那么A[i][j]
是否等效于从A
开始偏移i+5*j
?">
其他答案有点混乱,因为"N抵消"一词含糊不清。N个字节,还是N个int,或者N个int数组?
如果你在脑海中想象A
是一个长度为25的一维数组(我们称之为B
(,那么A[i][j]
将指定与B[i*5+j]
相同的对象。
要在代码中表达这一点,您需要编写:int *B = (int *)&A
。这将int的2-D数组别名为1-D数组。
注:。写int *B = (int *)A
是错误的,因为A
衰变为只有5个int的&A[0]
,所以B[6]
仍然是一个越界访问(未定义的行为(。如果您的编译器中没有启用边界检查,那么您可能不会注意到任何东西。
x[i][j]
总是与*(*(x+i)+j)
完全等价。。。但你必须记住,指针运算会考虑所指向类型的大小
double ary[NROWS][NCOLS];
ary[i]
(即*(ary+i)
(的大小为NCOLS*sizeof(double)
。在中
double** ptr;
ptr[i]
(即*(ptr+i)
(的大小为sizeof(double*)
。
在ary[i][j]
或ptr[i][j]
这两种情况下,都会提取正确的元素。
是。你走的是正确的路,但申报
double grid[5][5]
和
double **grid;
不同。第一个是声明double
类型的25个元素的2D数组,而第二个是声明指向double
类型的指针的指针。两者都不同。请注意,数组不是指针。
在第一种情况下,内存分配在堆栈上并且是连续的,因此编译器将grid[i][j]
优化为*(*(grid + i) + j)
,CCD_50进一步优化为*(*grid + (i*5 + j))
。
在第二种情况下,内存是在堆上分配的,malloc
不会创建连续内存。在这种情况下,编译器将grid[i][j]
优化为*(*(grid + i) + j)
,但并没有将其进一步优化为*(*grid + (i*5 + j))
。
除其他答案外:
当声明网格时,一个连续的内存块被分配为5*5倍,不多也不少;
这并不完全正确(少的部分总是正确的(,但有时你会发现,即使你在数组结束后访问内存,程序似乎也能工作(不会立即出错(。这意味着有时malloc可以分配比您要求的更多的内存。当然,这并不意味着你应该访问过多的内存。
是的,A[i][j]
所指的地址是否取决于A的声明方式。
用于计算array_variable[i][j]
的指针算术取决于列数(在第二维度上(:这是因为每行(第一维度(的所有元素都存储在一起,列数影响第二行、第三行等的数据存储位置。
这个URL声明,如果你有一个二维数组,声明为:
int [NUMBER_OF_ROWS][NUMBER_OF_COLUMNS]
然后:
x = y[a][b]
相当于:
x = *((int *)y + a * NUMBER_OF_COLUMNS + b);
网址:如何使用指针表达式访问C中二维数组的元素?
该URL表示(二维数组(的元素按行主顺序存储:例如,首先存储行1的所有元素。
http://www.cse.msu.edu/~cse251/electure11.pdf
这个URL有一个类似的计算,取决于第二个维度的大小:
http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/pointer.html
addr( & arr[ row ][ col ] ) = addr( arr ) + [ sizeof( int ) * COLS * row ]
+ [ sizeof( int ) * col ]
Q在C中,A[i][j]的含义取决于A的声明方式吗?
A不,没有。
如果你有,
int a[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
则a[i]
被评估为*(a+i)
。
如果你有
int ** create2DArray(int d1, int d2)
{
int i = 0;
int ** a = malloc(sizeof(*a)*d1);
for ( ; i != d1; ++i )
{
a[i] = malloc(sizeof(int)*d2);
}
return a;
}
int **a = create2DArray(3, 3);
则a[i]
仍然被评估为*(a+i)
。
无论它们是如何声明的,a[i][j]
都被评估为*(a[i] + j)
。
您仍然可以调用以下函数:
void func(int* p, int size)
{
int i = 0;
for ( ; i != size; ++i )
{
printf("%d ", p[i]);
}
printf("n");
}
使用
func(a[i]);
不管CCD_ 64是如何定义的。
a
的第一个定义和a
的第二个定义之间的主要区别在于如何为其分配内存。在第一种情况下,内存是在堆栈上分配的,并且是连续的。在第二种情况下,内存是在堆上分配的,并且很可能是不连续的。然而,如何使用它和编译器如何处理它是完全相同的。