在第二个等级指针上使用下标运算符

时间:2013-09-19 15:41:26

标签: c arrays pointers

我对C中指针和数组的问题感到困惑。

让我们先看一段代码:

//case 1
int **a;
a = (int **)malloc(sizeof(int*)*m);
for(i = 0; i < m; i++)
    a[i] = (int *)malloc(sizeof(int)*n);

//case 2
int b[m][n];

然后,我们知道,内存中b的布局如下:

b[0][0] b[0][1] ... ... b[m-1][n-1]

并且内存的布局如下:

a
 \
 _\|
 a[0]  a[1] ... a[m-1]
  |    |
  |    |
  |   \|/
  |   a[1][0] a[1][a] ... a[1][n-1]
 \|/
a[0][1] a[0][2] ... a[0][n-1]

所以,我的问题是:由于a[0..m-1][0..n-1]非常连续地存储在内存中,为什么我们可以在[]上使用下标运算符a?换句话说,为什么a[i][j]可以获得正确的元素,就像b[i][j]一样?

5 个答案:

答案 0 :(得分:3)

那是因为a[i][j]等于*(*(a+i) + j)。即使a未连续存储(可能),指针*(a+i)也会精确指向正确的位置(存储a[i][0]的位置)。

答案 1 :(得分:2)

因为a的元素连续存储在内存中,并且每个a[n]的元素都连续存储在内存中。执行int n = a[i][j]基本上等同于执行int * p = a[i]; int n = p[j];这两个维度在a的内存中可能与b的方式不相关并不重要。

答案 2 :(得分:2)

它们都有效,因为编译器会针对每种情况生成不同的内容。虽然确实单个维数的数组和指针以及类似的,双重维数的数组和指针指针都有相似之处。

因此,对于b的情况,因为编译器知道b是一个数组并且是一个连续的内存块,所以它生成的代码为:

// int c = b[i][j];
int c = *(b + (i*m+j);

但它为a生成不同的代码,因为它是指向指针的指针而不是连续的内存块,每个括号运算符都是一个解除引用运算符:

// int c  = a[i][j];
int c = *(*(a+i)+j);

答案 3 :(得分:2)

编译器分别处理表达式的每个部分。 a[i][j]b[i][j]之间的主要区别在于,由于a是指向int的指针,因此a[i]的地址计算会计算多个指针但是,由于bint的数组数组,因此b[i]的地址计算会计算多个数组。 C使用表达式每个部分的类型来确定如何评估它。

解释a[i][j]的步骤如下:

  • a是指向int的指针。
  • a[i]定义为*(a + i)
  • 总和a+i是除i分之外的a个元素。由于a指向指向int的指针,因此我们会将i指针指向int以确定新地址。
  • 由于sum是指向int指针的指针,因此将*应用于它会产生指向int的指针。
  • a[i][j]定义为*(a[i] + j)。我们已经评估了a[i]部分。
  • 总和a[i] + j是除j分之外的a[i]个元素。由于a[i]指向int个对象,我们会计算j int个对象以确定新地址。
  • 由于此总和是指向int的指针,因此将*应用于int会产生b[i][j]

解释b的步骤如下:

  • mn int b数组的数组。
  • 由于n是一个数组,因此会将其转换为指向其第一个元素的指针,即int b[i]的数组。
  • *(b + i)定义为b + i
  • 总和i是除b分之外的b个元素。由于n已转换为指向int i数组的指针,因此我们会计算n int n个数组以确定新地址
  • 由于sum是指向int *数组的指针,因此将n应用于它会产生一个int int的数组。
  • 由于此表达式是另一个数组,因此它将转换为指向其第一个元素的指针b[i][j]
  • *(b[i] + j)定义为b[i]。我们已经评估了b[i] + j部分。
  • 总和j是除b[i]分之外的int个元素。由于它指向j,我们会计算int int以确定新地址。
  • 由于此总和是指向*的指针,因此将int应用于{{1}}会产生{{1}}。

答案 4 :(得分:0)

a[i][j]

基本上我们说:

我们需要a指向的第i个指针,然后我们需要该内部指针指向的第j个int。

只使用这个概念我们可以写

foo(int **a, int rows, int cols){
//...
//acess a[i][j] ; 0<=i<rows, 0<=j<cols
}