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]); }
但是我收到了两个案例的相同结果。 因此,通过这个例子,我认为我们应该说:数组变量是第一个元素。 (因为它有相同的地址)
我不知道这是真还是错。 请教我。
数组变量表示数组占用的整个内存块,而不仅仅是数组的第一个元素。 因此array
与array[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 = array
,array
衰减为指针(&array
) - 在
void *var = function
,function
衰减为指针(&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 array
和sizeof 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;
p
和pa
指向a
的第一个元素,我们假设它在地址0x8000
。 执行完行后
p++; pa++;
但是, p
指向下一个整数 ( 0x8004
,假设4字节为int
),而pa
指向下一个10元素的整数数组 ; 也就是说,( 0x8028
)的最后一个元素之后的第一个整数。