2维数组的问题

4

我用C语言编写了以下代码:

#include<stdio.h>

int  main()
{
  int a[10][10]={1};
  //------------------------
  printf("%d\n",&a);
  printf("%d\n",a);
  printf("%d\n",*a);
  //-------------------------
  printf("%d",**a);

  return 0;
}

使用上述3个printf语句,我得到了相同的值。在我的机器上是2686384。但通过最后一个语句,我得到了1。
这不是出了什么问题吗?这些语句的意思是:
1. a变量的地址是2686384 2. 存储在a变量中的值为2686384 3. 指针a所指向的变量地址上(即2686384)存储的值为2686384。
这意味着a必须是一种指向自身的变量...
那么为什么*(*a)的输出是1?它为什么不被评估为*(*a)= *(2686384)= 2686384?

1
尝试使用此链接并了解二维数组的基础知识。[引用二维数组] (http://stackoverflow.com/questions/2923214/dreferencing-2-d-array?rq=1) - MarsRover
7个回答

2
#include<stdio.h>

int  main()
{
  // a[row][col]
  int a[2][2]={ {9, 2}, {3, 4} };
  // in C, multidimensional arrays are really one dimensional, but
  // syntax alows us to access it as a two dimensional (like here).

  //------------------------
  printf("&a            = %d\n",&a);
  printf("a             = %d\n",a);
  printf("*a            = %d\n",*a);
  //-------------------------

  // Thing to have in mind here, that may be confusing is:
  // since we can access array values through 2 dimensions,
  // we need 2 stars(asterisk), right? Right.

  // So as a consistency in this aproach,
  // even if we are asking for first value,
  // we have to use 2 dimensional (we have a 2D array)
  // access syntax - 2 stars.

  printf("**a           = %d\n", **a );         // this says a[0][0] or *(*(a+0)+0)
  printf("**(a+1)       = %d\n", **(a+1) );     // a[1][0] or *(*(a+1)+0)
  printf("*(*(a+1)+1)   = %d\n", *(*(a+1)+1) ); // a[1][1] or *(*(a+1)+1)
  // a[1] gives us the value on that position,
  // since that value is pointer, &a[i] returns a pointer value
  printf("&a[1]         = %d\n", &a[1]);
  // When we add int to a pointer (eg. a+1),
  // really we are adding the lenth of a type
  // to which pointer is directing - here we go to the next element in an array.

  // In C, you can manipulate array variables practically like pointers.
  // Example: littleFunction(int [] arr) accepts pointers to int, and it works vice versa,
  //          littleFunction(int* arr) accepts array of int.

  int b = 8;
  printf("b             = %d\n", *&b);

  return 0;

}

1

首先,关于数组...

除非它是sizeof_Alignof或一元&运算符的操作数0,或者是用于在声明中初始化另一个数组的字符串字面量,类型为"N个元素的T数组"的表达式将被转换("衰减")为类型为"T指针"的表达式,并且表达式的值将是数组中第一个元素的地址。

表达式&a的类型为"10个元素的10个元素int数组的指针",或int (*)[10][10]。表达式a的类型为"10个元素的10个元素int数组",根据上述规则衰减为"10个元素的int数组的指针",或int (*)[10]。最后,表达式*a(等同于a[0])的类型为"10个元素的int数组",再次根据上述规则衰减为"int指针"。

所有三个表达式的值相同,因为数组的地址和它的第一个元素的地址是相同的:&a[0][0] == a[0] == *a == a == &a。然而,这些表达式的类型不同,在执行指针算术时很重要。例如,如果我有以下声明:
int (*ap0)[10][10] = &a;
int (*ap1)[10]     =  a;
int *ip            = *a;

如果执行 ap0++,则会将 ap0 推进到下一个 10x10 的 int 数组,ap1++ 则会将 ap1 推进到下一个 10 元素的 int 数组(或者 a[1]),ip++ 则会将 ip 推进到下一个 int&a[0][1])。

**a 等同于 *a[0],也等同于 a[0][0]。这是数组 a 的第一个元素的值,类型为 int,值为 1(请注意,只有 a[0][0] 被初始化为 1;所有其他元素都被初始化为 0)。

请注意,应使用 %p 打印指针值:

printf("&a = %p\n", &a);
printf(" a = %p\n",  a);
printf("*a = %p\n", *a);

1

一个由数组名组成的表达式可以“衰变”为指向数组第一个元素的指针。因此,即使a的类型是int[10][10],它也可以衰变为int(*)[10]

现在,在表达式*a中发生了这种衰变。因此,该表达式的类型为int[10]。重复相同的逻辑,这又衰变为int*,因此**a是一个int,它还是数组a的第一个元素的第一个元素,即1

其他三个打印语句分别打印出数组、数组的第一个元素和数组的第一个元素的第一个元素的地址(当然都是相同的地址,只是不同的类型)。


这里的问题是如何让a和*a与**a共享同一内存位置。 - Shobhit Saxena
@ShobhitSaxena:它们并不相同。相反,&aa*a都具有相同的数字,这代表了**a的位置。原因与“特拉法加广场”、“伦敦”和“英国”都是我当前位置的正确描述一样。 - Kerrek SB

0
如果您将a定义为T a[10](其中T是某个typedef),那么一个简单的未装饰的a表示数组开始的地址,与&a[0]相同。它们都具有类型T*&a也是数组开头的地址,但它的类型是T**
在多维数组存在的情况下,事情变得更加棘手。为了看清发生了什么,使用typedef将事物分解成较小的块会更容易。因此,您实际上编写了
typedef int array10[10];
array10 a[10];

[读者练习:变量a的类型是什么?(它不是int **)]

**a正确地计算为数组a中的第一个int


0
来自C99标准

考虑由声明定义的数组对象

int x[3][5];

这里 x 是一个 3 × 5 的 int 类型数组;更准确地说,x 是由三个元素对象构成的数组,每个元素对象都是由五个 int 类型元素构成的数组。在表达式 x[i] 中,它等效于 (*((x)+(i))),x 首先被转换为指向五个 int 对象初始数组的指针。然后,根据 x 的类型来调整 i,这从概念上来说涉及将 i 乘以指针所指向的对象大小,即五个 int 对象的数组大小。然后进行加法运算并应用间接性,以产生一个由五个 int 组成的数组。当在表达式 x[i][j] 中使用时,该数组依次被转换为指向 int 元素的第一个指针,因此 x[i][j] 产生一个 int。

所以,

初始数组将只有 x[0][0]。

所有的 x、&x 和 *x 都将指向 x[0][0]。


0

不,你的代码没有任何问题。只是你的思路有些偏差...我越想越觉得这很难解释,所以在我开始之前,请记住以下几点:

  1. 数组不是指针,不要将它们视为同一类型。
  2. []是一个运算符。它是一个移位和引用运算符,因此当我写printf("%d",array[3]);时,我正在进行移位和引用操作。

因此,一个数组(让我们首先考虑一维数组)存储在内存中的某个位置:

int arr[10] = {1};
//Some where in memory---> 0x80001f23
                            [1][1][1][1][1][1][1][1][1][1]

所以如果我说:

*arr; //this gives the value 1

为什么呢?因为它与arr[0]是一样的,它给我们提供了数组起始地址处的值。这意味着:
arr; // this is the address of the start of the array

那么这给我们带来了什么?

&arr; //this will give us the address of the array. 
      //which IS the address of the start of the array
      //this is where arrays and pointers really show some difference

所以arr == &arr;。数组的“工作”是保存数据,数组不会“指向”任何其他东西,因为它保存着自己的数据。而指针的工作是指向其他东西:
int *z;     //the pointer holds the address of someone else's values
z = arr;    //the pointer holds the address of the array
z != &z;    //the pointer's address is a unique value telling us where the pointer resides
            //the pointer's value is the address of the array

编辑: 另一种思考这个问题的方式:

int b;   //this is integer type
&b;      //this is the address of the int b, right?

int c[]; //this is the array of ints
&c;      //this would be the address of the array, right?

所以这很容易理解,那么这个呢:

*c;   //that's the first element in the array

那一行代码告诉你什么?如果我取消引用 c ,那么我得到一个int。这意味着只有简单的 c 是一个地址。因为它是数组的开头,所以它是数组的地址,因此:
c == &c;

这就是我的问题所在吗?arr ==&arr怎么可能??你如何允许一个指针存储它自己的内存地址,然后对其进行多次解引用,并说那是存储在a [0]中的值。 - Shobhit Saxena
@ShobhitSaxena - 你没有听懂第一点。arr不是一个指针。数组不等于指针。就这样。看看我的修改。那样清楚些吗?我觉得那可能更明确一些。 - Mike
@ShobhitSaxena 请记住,取值运算符(*)并不是指针特有的。它是作用于内存地址的运算符。我可以这样做:int x =0;然后 printf("%d",*(&x));,虽然没有什么意义,但它可以打印出0,而且x并不是一个指针。 - Mike

0
首先,如果您想打印出指针的值,请使用%p - 如果您正在使用64位机器,则int几乎肯定比指针小。 **a 是双重解引用实际上是一个 int ** ,所以您最终得到的是第一个子数组的第一个元素:1。

但是y * a,a会给出相同的值...?? - Shobhit Saxena

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接