C指针:数组变量

我在书中看到了这一点(以及互联网上的许多消息来源):

数组变量指向数组中的第一个元素。

如果这是真的,那么数组变量和第一个元素是不同的 。 对?

它意味着通过下面的代码,它将产生两个不同的结果:

int main(){ char msg[] = "stack over flow"; printf("the store string is store at :%p\n",&msg); printf("First element: %p\n",&msg[0]); } 

但是我收到了两个案例的相同结果。 因此,通过这个例子,我认为我们应该说:数组变量第一个元素。 (因为它有相同的地址)

我不知道这是真还是错。 请教我。

数组变量表示数组占用的整个内存块,而不仅仅是数组的第一个元素。 因此arrayarray[0] (参见sizeof array / sizeof array[0] )。 但是数组的第一个元素与数组本身位于相同的内存地址。

说数组指向第一个元素也是不正确的,在大多数情况下,数组表达式衰变为指向其第一个元素的指针,但它们是不同的东西(例如cf.of sizeof )。

它们指向相同的地址,即printf将显示相同的值,但它们具有不同的类型。

  • &msg的类型是char(*)[16] ,指向char的数组16的指针
  • &msg[0]的类型是char * ,指向char的指针

一种廉价的测试方法是做一些指针算法。 尝试打印&msg + 1

这个C FAQ可能有用。

数组变量整个数组。 它变成指向数组第一个元素的指针。

如果你看看类型:

  • msg的类型为char [16]
  • &msg的类型为char (*)[16]
  • &msg[0]的类型为char *

因此,在msg可以衰减到数组的上下文中,例如当作为参数传递时,其值将等于&msg[0]

让我画出这个:

 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+--+ |s|t|a|c|k| |o|v|e|r| |f|l|o|w|\0| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+--+ 

想象一下这个数组的起点,其中's'位于地址0x12345678

  • msg本身,指的是整个16字节的内存。 就像你说int a;a指的是4个字节的内存。
  • msg[0]是该16字节的第一个字节。
  • &msg是数组开始的地址: 0x12345678
  • &msg[0]是数组的第一个元素的地址: 0x12345678

这就是为什么&msg&msg[0]的值相同,但它们的类型不同。

现在的问题是, msg本身并不是一等公民 。 例如,您无法分配数组。 这就是为什么在大多数情况下,数组会衰减成指针。

如果你知道函数指针,这非常相似:

 int array[10]; int function(int); 
  • int *var = arrayarray衰减为指针( &array
  • void *var = functionfunction衰减为指针( &function

注意,在函数指针的情况下,我们喜欢保留类型,所以我们写:

 int (*var)(int) = function; 

同样,您可以使用数组:

 int (*var)[10] = array; 
 char myChar = 'A' char msg[] = 'ABCDEFGH' 

当你输入myChar时,你会获得价值。 但是使用msg你会获得指向第一个char的指针(对于必须使用msg [x]的值)

 msg = &msg[0] 

我想这可以帮助你理解。

这样看:

 &msg = 0x0012 &msg[0] = 0x0012 &msg[1] = 0x0013 

在这种情况下&msg[1]指向msg+1 。 当您引用&msg&msg[0]您指的是相同的内存地址,因为这是指针启动的​​位置。 增加数组变量会使指针增加+1,因为char变量的大小只有1个字节。

如果你用一个整数做同样的技巧,你会将指针增加+4个字节,因为整数是4个字节的大小。

使用数组表达式时,编译器会将其转换为指向第一个元素的指针。 这是由1999 C标准在6.3.2.1中指定的显式转换。这对您来说很方便,因此您不必编写&array[0]来获取指向第一个元素的指针。

转换发生在所有表达式中,除非数组表达式是sizeof的操作数或一元&或是用于初始化数组的字符串文字。

您可以通过sizeof arraysizeof array[0]看到数组及其第一个元素是不同的。

在大多数情况下,数组类型的表达式 (“N元素数组T ”)将被/转换为/“decay”替换为指针类型的表达式(“指向T指针”),以及表达式的将是数组中第一个元素的地址。

所以,假设声明

 int a[10]; 

表达式a的类型是“10元素数组的int ”或int [10] 。 但是,在大多数情况下,表达式的类型将转换为“指向int指针”或int * ,表达式的值将等同于&a[0]

此规则的例外情况是数组表达式是sizeof或一元&运算符的操作数,或者是用于初始化声明中另一个数组的字符串文字。

因此,根据我们上面的声明,以下所有内容都是正确的:

  表达类型衰变为价值
   ---------- ---- --------- -----
           一个int [10] int *的第一个元素的地址
           &a int(*)[10] n /数组的地址,即
                                            与第一个地址相同
                                            元件
        &a [0] int * n / a的第一个元素的地址
           * a n / a值为[0]
     sizeof a size_t n /数组中的字节数 
                                             (10 * sizeof(int))
    sizeof&size_t n /指针中的字节数 
                                           一个int数组
    sizeof * a size_t n / int中的字节数
 sizeof&a [0] size_t n /指向int的指针中的字节数

请注意,表达式a&a&a[0]都具有相同的a的第一个元素的地址),但类型不同。 类型很重要。 假设如下:

 int a[10]; int *p = a; int (*pa)[10] = &a; 

ppa指向a的第一个元素,我们假设它在地址0x8000 。 执行完行后

 p++; pa++; 

但是, p指向下一个整数0x8004 ,假设4字节为int ),而pa指向下一个10元素的整数数组 ; 也就是说,( 0x8028 )的最后一个元素之后的第一个整数。