简体   繁体   English

C中数组变量与指针的地址和内容

[英]Address and contents of array variable vs pointer in C

I know array in C does essentially behaves like a pointer except at some places like ( sizeof() ). 我知道C语言中的数组的确在某些情况下的行为类似于指针,除了( sizeof() )之外。 Apart from that pointer and array variables dont differ except in their declaration. 除此之外,指针和数组变量的区别仅在于声明。

For example consider the two declarations: 例如,考虑以下两个声明:

int arr[] = {11,22,33};
int *arrptr = arr; 

Now here is how they behave same: 现在,它们的行为相同:

printf("%d  %d", arrptr[0], arr[0]);  //11  11
printf("%d  %d", *arrptr, *arr);      //11  11

But here is one more place I found they differ: 但是在这里我发现它们又有所不同:

//the outputs will be different on your machine
printf("%d  %d", &arrptr, &arr);   //2686688  2686692 (obviously different address)
printf("%d  %d", arrptr, arr);     //2686692  2686692 (both same)

Here the issue is with last line. 这是最后一行的问题。 I understand that arrptr contains the address of arr . 我知道arrptr包含arr的地址。 Thats why the first address printed in last line is 2686692 . 这就是为什么在最后一行打印的第一个地址是2686692 I also understand that logically the address ( &arr ) and contents ( arr ) of arr should be same unlike arrptr . 我也理解逻辑上,与arrptr不同, arr的地址( &arr )和内容( arr )应该相同。 But then whats exactly that which (internally at implementation level) that makes this happen? 但是,使这种情况发生的确切原因(在实施级别内部)又是什么呢?

When the unary & operator is applied to an array, it returns a pointer to an array. 将一元&运算符应用于数组时,它将返回指向数组的指针。 When applied to a pointer, it returns a pointer to a pointer. 当应用于指针时,它将返回指针。 This operator together with sizeof represent the few contexts where arrays do not decay to pointers. 该运算符与sizeof一起表示数组不会衰减到指针的少数上下文。

In other words, &arrptr is of type int ** , whereas &arr is of type int (*)[3] . 换句话说, &arrptrint **类型,而&arrint (*)[3] &arrptr is the address of the pointer itself and &arr is the beginning of the array (like arrptr ). &arrptr是指针本身的地址, &arr是数组的开头(如arrptr )。

The subtle part: arrptr and &arr have the same value (both point to the beginning of the array), but are of a different type. 细微的部分: arrptr&arr具有相同的值(均指向数组的开头),但类型不同。 This difference will show if you do any pointer arithmetic to them – with arrptr the implied offset will be sizeof(int) , whereas with &arr it will be sizeof(int) * 3 . 如果您对它们执行任何指针算术,则此差异将显示出来–使用arrptr ,隐式偏移量将为sizeof(int) ,而使用&arr其隐含偏移量将为sizeof(int) * 3

Also, you should be using the %p format specifier to print pointers, after casting to void * . 另外,在转换为void *之后,应该使用%p格式说明符来打印指针。

I know array in C does essentially behaves like a pointer except at some places like (sizeof()). 我知道C语言中的数组的行为本质上类似于指针,除了(sizeof())这样的地方。 Apart from that pointer and array variables dont differ except in their declaration. 除此之外,指针和数组变量的区别仅在于声明。

This is not quite true. 这不是真的。 Array expressions are treated as pointer expressions in most circumstances, but arrays and pointers are completely different animals. 在大多数情况下,数组表达式被视为指针表达式,但是数组和指针是完全不同的动物。

When you declare an array as 当您将数组声明为

T a[N];

it's laid out in memory as 它作为

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

One thing immediately becomes obvious - the address of the first element of the array is the same as the address of the array itself. 一件事立即变得很明显-数组第一个元素的地址与数组本身的地址相同。 Thus, &a[0] and &a will yield the same address value , although the types of the two expressions are different ( T * vs. T (*)[N] ), and the value may possibly adjusted based on type. 因此, &a[0]&a将产生相同的地址 ,尽管两个表达式的类型不同( T *T (*)[N] ),并且该值可能会根据类型进行调整。

Here's where things get a little confusing - except when it is the operand of the sizeof or unary & operator, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T " will be converted ("decay") to an expression of type "pointer to T ", and the value of the expression will be the address of the first element of the array. 在这里,事情变得有些混乱-除非它是sizeof或一元&运算符的操作数,或者是用于初始化声明中的字符数组的字符串文字,否则类型为“ N的T元素数组”的表达式将被转换(“衰减”)为类型“ pointer to T ”的表达式,该表达式的值将是数组第一个元素的地址。

This means the expression a also yields the same address value as &a[0] and &a , and it has the same type as &a[0] . 这意味着表达式a 产生与&a[0]&a相同的地址值,并且具有与&a[0]相同的类型 Putting this all together: 全部放在一起:

Expression        Type        Decays to         Value
----------        ----        ---------         -----
         a        T [N]       T *               Address of a[0]
        &a        T (*)[N]    n/a               Address of a
        *a        T           n/a               Value of a[0]
      a[i]        T           n/a               Value of a[i]
     &a[i]        T *         n/a               Address of a[i]
  sizeof a        size_t      n/a               Number of bytes in a

So why does this conversion rule exist in the first place? 那么,为什么此转换规则首先存在呢?

C was derived from an earlier language called B (go figure). C是从一种称为B的早期语言派生而来的。 B was a typeless language - everything was treated as basically an unsigned integer. B是一种无类型的语言-基本上所有内容都被视为无符号整数。 Memory was seen as a linear array of fixed-length "cells". 内存被视为固定长度“单元”的线性阵列。 When you declared an array in B, an extra cell was set aside to store the offset to the first element of the array: 当您在B中声明一个数组时,一个额外的单元格被放置在一边,以将偏移量存储到该数组的第一个元素:

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

The array subscript operation a[i] was defined as *(a + i) ; 数组下标运算a[i]定义为*(a + i) that is, take the offset value stored in a , add i , and dereference the result. 也就是说,取存储在a的偏移量值,加上i ,然后取消对结果的引用。

When Ritchie was designing C, he wanted to keep B's array semantics, but couldn't figure out what to do with the explicit pointer to the first element, so he got rid of it. 当Ritchie设计C时,他想保留B的数组语义,但无法弄清楚该指向第一个元素的显式指针的作用,因此他放弃了它。 Thus, C keeps the array subscripting definition a[i] == *(a + i) (given the address a , offset i elements from that address and dereference the result), but doesn't set aside space for a separate pointer to the first element of the array - instead, it converts the array expression a to a pointer value. 因此,C保留数组下标定义a[i] == *(a + i) (给定地址a ,从该地址偏移i元素并取消引用结果),但没有为单独的指针留出空间数组的第一个元素-而是数组表达式a转换为指针值。

This is why you see the same output when you print the values of arr and arrptr . 这就是为什么在打印arrarrptr的值时看到相同的输出的arrptr Note that you should print out pointer values using the %p conversion specifier and cast the argument to void * : 请注意,您应该使用%p转换说明符打印出指针值,并将参数转换为void *

printf( "arr = %p, arrptr = %p\n", (void *) arr, (void *) arrptr );

This is pretty much the only place you need to explicitly cast a pointer value to void * in C. 这几乎是您唯一需要在C中将指针值void *void *的地方。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM