【问题标题】:Why won't *num show the zeroth element value?为什么 *num 不显示第零个元素值?
【发布时间】:2014-10-27 13:41:46
【问题描述】:

在这段代码中:

#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", &amp;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])?

【问题讨论】:

  • 你能用编辑器编辑你的消息来区分你的代码吗?
  • 首先,您的程序展示了未定义行为 (UB)。每当您想打印变量的地址时,您必须使用 %p,而不是 %d(整数)。
  • 在C构造中*num表示从地址num取值,类似于num[0]
  • @MikeMinaev 我的问题是,如果 num 是第一个示例中的指针,那么它应该有自己的地址但与 num[0] 具有相同的地址,为什么?第二个问题是,在第二个示例中,*num 显示的是地址而不是第零个元素的值,为什么?它们都有相同的地址(num,num[0],num[0][0])

标签: c arrays pointers address-operator


【解决方案1】:

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

对于这样的声明

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 的 N 元素数组”类型的 表达式 将被转换(“衰减”)为表达式类型为“指向T”的指针,表达式的值将是数组第一个元素的地址,除了,当数组表达式为以下之一时:

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

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

表达式&amp;a[0] 的类型为“指向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] );

编辑

回答 cmets 中的问题:

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

不完全是。

假设声明如下

int arr[10][10];

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

T 的 N 元素数组”类型的表达式衰减为“指向T 的指针”类型;如果T 是数组类型,则结果是“指向数组的指针”,而不是“指向指针的指针”。

【讨论】:

  • 1sec ..数组和数组名有什么区别?它们是不同的东西吗?
  • 首先感谢约翰以非常详细的方式给了我真正帮助我的答案。据我所知,你的回答(如果我错了,请纠正我)是 num,num [] 和 num[][] 都是不同的东西。类型不同。这里 num 衰减并成为指向指针的指针,而 num[] 衰减并成为指向 int 的指针,而 num[][] 是 int。对吗?
  • @JohnBode,感谢您对我的回答和这篇详细帖子的更正。老实说,我为自己的错误知识感到尴尬,不知道我是如何错误地学习的。
【解决方案2】:

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

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

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

【讨论】:

  • 感谢 Yu 的回复。但我的问题是 num,num[0] 和 num[0][0] 这三个东西都有相同的地址,那么为什么不只有 *num 呢?跨度>
  • 简单来说就是因为它们的值相同,但类型不同。 SO中有很多关于这个的问题,尝试在这里搜索,他们的解释会比我在评论中解释的要好得多。
  • 你能告诉我我在搜索框中写了什么来搜索它。你真的很棒:)
  • 非常感谢Yu。如果我通过这个答案理解整个事情,我会回复你。再次感谢:)
【解决方案3】:

看看数组长什么样:

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] 指的是第一个数组的第一个元素。

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

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

【讨论】:

  • 你的意思是在我的例子中 num[][] 衰减并成为指向指针的类型指针,num[] 是指向 int 的类型指针,而 num[][] 是 int ?跨度>
  • num 衰减为指向其第一个元素的指针,指向int[2] 的指针。 num[0] 也衰减为指向其第一个元素的指针,指向 int 的指针。而num[0][0] 本身就是int
  • 那么您对我的评价如何。我是对还是错? :D :)
【解决方案4】:

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

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

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

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

现在问题来了:

考虑一维数组:

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

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

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

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

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

这里,当你写数组的名字,如@9​​87654330@,你得到的是这个数组的第一个st元素的地址,在这种情况下就是这个的地址0th 索引:

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

现在当你执行&amp;arr 时,你得到的是整个块的基地址,即这个基地址:

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

现在,如果您执行*arr,在一维数组中它会为您提供 1st 元素内的值,但在二维数组中,每个索引内的值实际上是一个一维数组,因此你会得到这个数组的地址:

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

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

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

编辑 1:

正如其他用户提醒我的那样,尽管我已经详细解释了 what thingy 的含义,但似乎有些地方有些混乱。但只是为了证明这一点:

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

a&amp;a 的类型将有所不同,正如已经在答案中所述。如果执行指针算术,将能够知道这一点。尝试执行a + 1&amp;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指向下一个内存位置。

【讨论】:

  • 我真的很感激你。我的意思是你解释问题的方式真的很棒。虽然我有一些问题,但我认为它会被清除,因为我会阅读更多。你能建议我吗任何可以消除我对数组中整个指针的疑虑的事情
  • Understanding and Using Pointers in C by Richard Reese 是一本好书。尽管首先尝试从您刚刚遵循的任何教科书中查看指针。有一次,你觉得,你有点舒服,有了它,然后慢慢地去做一些事情。如果您有任何疑问,请随意。如果在我的知识范围内,一定会尽力指导你。因为我也是学习者。刚开始学习这门语言(Kerninghan 和 Richie 的 C 编程语言是一个很好的起点(这是我目前正在关注的))。其余的,你最欢迎并保持微笑:-)
  • "a 等于 &a" 这不是真的。它们的地址相同,但类型不同。
  • @glglgl:我的参考是地址,它们是如何存储在内存中的。谢谢你让我理解,关于一些混乱,我的回答的后半部分澄清了它们的不同之处。当我进入我的系统时将很快更新:-)
【解决方案5】:

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

查看此代码:

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

【讨论】:

    猜你喜欢
    • 2017-09-18
    • 2022-01-11
    • 2018-10-07
    • 1970-01-01
    • 2023-02-03
    • 1970-01-01
    • 1970-01-01
    • 2018-09-10
    • 1970-01-01
    相关资源
    最近更新 更多