为什么赢得&#t; * num显示第0个元素值?

时间:2014-10-27 13:41:47

标签: c arrays pointers address-operator

在此代码中:

#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;
}

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

5 个答案:

答案 0 :(得分: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语言中有一条规则,即“{元素数组T”类型的表达式将被转换(“衰变”)到类型为“T的指针”的表达式,当数组表达式为以下之一时,表达式的值将是数组的第一个元素的地址,之外: / p>

  • 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的类型为“{-1}}的N元素数组”;它不是一元T&运算符的操作数,因此它被转换为指向数组第一个元素的指针,并且它的值是该元素的地址。

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

表达式a具有类型“指向&a[0]的指针”,并明确指向数组的第一个元素。同样,此值将与前两个表达式相同。

对于2D数组

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(这几乎是唯一一次将指针显式转换为{ {1}}):

(void *)

修改

回答评论中的问题:

  

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

不完全。

假设声明如

void *

然后表达式printf( " &a yields %p\n", (void *) &a ); printf( " a yields %p\n", (void *) a ); printf( "&a[0] yields %p\n", (void *) &a[0] ); 将衰减为int arr[10][10]; (指向arr的10个元素数组的指针),而不是int (*)[10];再次参考上表。否则你是对的; int会拒绝输入int **arr[i]会输入int *类型。

“{元素数组arr[i][j]”类型的表达式衰减以键入“指向int的指针”;如果T是数组类型,则结果是“指向数组的指针”,而不是“指针指针”。

答案 1 :(得分:1)

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

要获得num[0][0],您需要**num

printf("%d\n", **num);

答案 2 :(得分:1)

查看数组的外观:

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

最好写成

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

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

在记忆中,它们看起来像

20    30    40    50

但区别在于num指的是整个数组,num[0]指向第一个“part-array”,num[0][0]指向第一个数组的第一个元素。

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

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

答案 3 :(得分: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(对于此示例)时,您将获得1 st 元素的起始地址。虽然如果你写&arr,那么你得到整个块的起始地址(这包括数组的所有元素)。现在当你写*arr时,你实际上得到了这个数组的1 st 元素内的值。

现在考虑这个二维数组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

这里,当您编写数组的名称时,arr,您获得的是此数组的1 st 元素的地址,在这种情况下将是地址这个0 th 指数:

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,在1维数组中它会给出1 st 元素内的值,尽管在二维数组中,每个索引中的值是实际上是一个1维数组,因此你将得到这个数组的地址:

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

现在,如果您执行**arr,那么您实际上将获得1 st 元素内的值,即11

我希望它能解决一些疑问: - )

编辑1:

由于我的参与者,同事们看来,某处似乎有点混乱,尽管我已经详细解释了什么是什么意思。但只是为了证明这一说法:

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

如前所述,答案中a&a的类型将有所不同。如果一个人执行指针算术,人们就能知道。尝试执行a + 1&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指向下一个内存位置。

答案 4 :(得分:-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