-1

在这段代码中:

#include<stdio.h>
  int main()
  {
    int num[2] = {20, 30};
    printf("%d", num);
    printf("%d", &num[0]);
    return 0;
  }

据我所知,printf 语句都将打印第一个元素的地址,num因为在第一个语句中,num是一个指向 int 的指针。

但是如果num是一个指针,那么它也应该有任何地址,但在打印它的地址(使用printf("%d", &num))时,它显示了第一个元素的地址。

在二维数组中,整个事情也变得混乱:

#include<stdio.h>
int main(void)
{
    int num[ ] [2]={20,30,40,50};
    printf("%d",*num);
    return 0;
}

该程序正在打印第零个元素的地址,即num[0][0]. 但它为什么这样做呢?为什么不打印存储在其中的值,因为它们都有相同的地址(num,num[0]num[0][0])?

4

5 回答 5

5

首先要做的事;数组变量不是指针;他们存储任何地址。

对于声明,例如

T a[N];

内存将被布置为

         +---+
   a[0]: |   |
         +---+
   a[1]: |   |
         +---+
          ...
         +---+
 a[N-1]: |   |
         +---+

对于 2D MxN 数组,它看起来像

              +---+
     a[0][0]: |   |
              +---+
     a[0][1]: |   |
              +---+
               ...
              +---+
   a[0][N-1]: |   |
              +---+
     a[1][0]: |   |
              +---+
     a[1][1]: |   |
              +---+
               ...
              +---+
 a[M-1][N-1]: |   |
              +---+

对于 3D 和更高的阵列,该模式应该很明显。

a如您所见,没有为包含第一个元素地址的单独变量预留存储空间;相反,C 语言中有一条规则,“N 元素数组”类型的表达式T将被转换(“衰减”)为“指针T”类型的表达式,并且表达式的值将是数组的第一个元素,除非数组表达式是以下之一:

  • 运算符的sizeof操作数
  • 一元运算&符的操作数
  • 运算符的操作数_Alignof(C99 及更高版本)
  • 用于在声明中初始化数组的字符串文字

所以鉴于声明

T a[N];

以下所有情况均属实:

Expression         Type        Decays to         Value
----------         ----        ---------         -----
         a         T [N]       T *               address of first element, &a[0]
        *a         T           n/a               value stored in first element
        &a         T (*)[N]    n/a               address of the array, which is 
                                                   the same as the address of the
                                                   first element of the array
      a[i]         T           n/a               value stored in the i'th element
     &a[i]         T *         n/a               address of the i'th element
  sizeof a         size_t      n/a               total number of bytes used by the
                                                   array
 sizeof *a         size_t      n/a               total number of bytes used by the
                                                   first element of the array
 sizeof &a         size_t      n/a               total number of bytes used by a 
                                                   pointer to the array 

表达式a的类型为“N 元素数组T”;它不是一元&sizeof运算符的操作数,因此它被转换为指向数组第一个元素的指针,并且它的值是该元素的地址。

表达式&a的类型为“指向 N 元素数组的指针T”;因为a是一元运算&符的操作数,所以不应用上面的转换规则(这就是表达式有 typeT (*)[N]而不是 的原因T **)。但是,由于数组的地址与数组的第一个元素的地址相同,因此它产生的与表达式相同a

该表达式&a[0]的类型为“pointer to T”,并显式指向数组的第一个元素。同样,此值将与前两个表达式相同。

对于二维数组

T a[M][N];

以下所有情况均属实:

Expression         Type        Decays to         Value
----------         ----        ---------         -----
         a         T [M][N]    T (*)[N]          address of first subarray, a[0]
        *a         T [N]       T *               address pf first subarray, a[0]
        &a         T (*)[M][N] n/a               address of the array, which is 
                                                   the same as the address of the
                                                   first subarray, which is the same
                                                   as the address of the first element
                                                   of the first subarray.
      a[i]         T [N]       T *               address of first element of i'th
                                                   subarray
     *a[i]         T           n/a               value of first element of i'th subarray
     &a[i]         T (*)[N]    n/a               address of the i'th subarray
  sizeof a         size_t      n/a               total number of bytes used by the
                                                   array
 sizeof *a         size_t      n/a               total number of bytes used by the
                                                   first subarray
 sizeof &a         size_t      n/a               total number of bytes used by a 
                                                   pointer to the array 

最后说明:要打印出指针值,请使用%p转换说明符并将参数转换为(void *)(这几乎是唯一一次认为明确地将指针转换为正确的时间void *):

printf( "   &a yields %p\n", (void *) &a );
printf( "    a yields %p\n", (void *) a );
printf( "&a[0] yields %p\n", (void *) &a[0] );

编辑

要回答评论中的问题:

num,num[] 和 num[][] 都是不同的东西。类型不同。这里 num 衰减并成为指向指针的指针,而 num[] 衰减并成为指向 int 的指针,而 num[][] 是 int。正确的?

不完全的。

假设像这样的声明

int arr[10][10];

然后表达式arr将衰减为类型int (*)[10](指向 10 元素数组的指针int),而不是int **; 再次参考上表。否则你是对的;arr[i]会衰减到 type int *,并且arr[i][j]会有 type int

“N-element array of T”类型的表达式衰减为类型“pointer to T”;如果T是数组类型,则结果是“指向数组的指针”,而不是“指向指针的指针”。

于 2014-10-27T14:57:37.227 回答
1

看看数组的样子:

int num[ ] [2]={20,30,40,50};

最好写成

int num[][2]={{20,30},{40,50}};

它是一个包含 2 个元素的数组。这两个元素同样是具有 2 个整数的数组。

在记忆中,它们看起来像

20    30    40    50

但不同之处在于它num指的是整个数组、num[0]第一个“部分数组”和num[0][0]第一个数组的第一个元素。

它们具有相同的地址(因为它们从同一个地方开始),但它们具有不同的类型。

也就是说,地址不是指针唯一重要的东西,类型也很重要。

于 2014-10-27T14:19:50.763 回答
1

在第二个例子中,num是一个二维数组,或者说一个数组数组。确实*num是它的第一个元素,但是这个第一个元素本身就是一个数组。

要得到num[0][0],你需要**num

printf("%d\n", **num);
于 2014-10-27T13:46:42.337 回答
0

数组实际上不是指针,尽管它们的行为方式有点相似,但并非总是如此。

假设你有这个数组和一个指针:

int a[] = {1, 2, 3};
int i = 19;
int *ptr = &i;

现在在这里,但对于指针 ( ) a is equal to &a,情况并非如此。ptr is not equal to &ptr

现在来到这个问题:

考虑一个一维数组:

int arr[] = {11, 19, 5, 9};

在这里,这个数组元素存储在连续的内存位置。说,起始地址0

 ---------------------
| 11 |  19 |  5 |  9 |
 ---------------------
0   4     8    12   16

现在,当您写入数组的名称 arr(对于此示例)时,您将获得第一个元素的起始地址。虽然如果你写&arr,那么你会得到整个块的起始地址(这包括数组的所有元素)。现在,当您编写 时*arr,您实际上得到了该数组的第一个元素内的值。

现在考虑这个二维数组 arr[][4] = {{11, 19, 5, 9}, {5, 9, 11, 19}}:

0   4     8    12   16   -> These are memory addresses
 ---------------------
| 11 |  19 |  5 |  9 | ----> These values represent the values inside each index
 ---------------------
| 5  |   9 | 11 | 19 |
 ---------------------
16   20   24    28   32

在这里,当你写数组的名称 asarr时,你得到的是这个数组的第一个元素的地址在这种情况下将是这个第 0索引的地址:

0                           16                32
 ----------------------------------------------
| 0<sup>th</sup> index | 1<sup>st</sup> index |
 ----------------------------------------------

现在,当你这样做时&arr,你得到的是整个块的基地址,即它的基地址:

0   4     8    12   16 
 ---------------------
| 11 |  19 |  5 |  9 | 
 ---------------------
| 5  |   9 | 11 | 19 |
 ---------------------
16   20   24    28   32

现在,如果你这样做*arr,在一维数组中它会给你第一个元素内的值虽然在二维数组中,每个索引内的值实际上是一个一维数组,因此你会得到这个地址大批:

0   4     8    12   16 
 ---------------------
| 11 |  19 |  5 |  9 | 
 ---------------------

现在,如果您这样做**arr,那您实际上将获得第一个元素内的值11.

我希望它能消除一些疑问:-)

编辑1:

正如其他用户提醒我的那样,尽管我已经详细解释了什么是什么意思,但似乎在某处有些混乱。但只是为了证明这一点:

现在这里 __a 等于 &a__,但对于指针(__ptr 不等于 &ptr__),情况并非如此。

正如已经在答案中所述,两者的类型都会有所a不同。&a如果一个人执行指针算术,一个人将能够知道这一点。尝试执行a + 1and &a + 1,它们对指针算术的反应肯定会给出一个好主意。

考虑一维数组:

int arr[] = {11, 19, 5, 9};


 ---------------------
| 11 |  19 |  5 |  9 |
 ---------------------
0   4     8    12   16

我们不能这样做a++,但对于指针:

int i = 4;
int *ptr = &i;

我们可以执行ptr++,这将ptr指向下一个内存位置。

于 2014-10-27T15:07:03.117 回答
-2

我认为它的结果意味着数组不是真正的指针,而是在某些需要指针的上下文中转换为指针,例如传递给需要指针参数的函数。

看到这段代码:

void test(int* num) {
     printf("test\n");
     printf("%p\n",num);
     printf("%p\n",&num);
     printf("%p\n",&num[0]);
  }

int main(){
         int num[2]={20,30};
         test(num);
         printf("main\n");
         printf("%p\n",num);
         printf("%p\n",&num);
         printf("%p\n",&num[0]);
         //other();
         return 0;
}

输出是:

test
0x7fff7a422300
0x7fff7a4222e8   //LOOK THIS! Is diferent from main!
0x7fff7a422300
main
0x7fff7a422300
0x7fff7a422300
0x7fff7a422300
于 2014-10-27T14:14:58.637 回答