鉴于此代码:
#include <stdio.h>
int main()
{
char a[3][5] = {2, 7, 3, 9, 5,
1, 2, 3, 4, 5,
6, 7, 8, 9, 10};
printf("sizeof(a)=%d, sizeof(&a)=%d, sizeof(a[0])=%d\n", sizeof(a),
sizeof(&a), sizeof(a[0]));
printf("a+1=%p, a[0]+1=%p, &a+1=%p, &a[0]+1=%p\n", a+1, a[0] + 1, &a+1,
&a[0]+1);
}
如果a从0x1000开始,而char是1个字节,那么输出是什么?真正的问题是为什么这些数字被打印,尤其是最后一行。
我已经执行了程序并观察了输出。
sizeof(a)=15, sizeof(&a)=8, sizeof(a[0])=5 a+1=0x7ffc7d4dfc15, a[0]+1=0x7ffc7d4dfc11, &a+1=0x7ffc7d4dfc1f, &a[0]+1=0x7ffc7d4dfc15
这里的问题是为什么不同的数字。例如:为什么[0] +1和&amp; a + 1?
之间存在差异答案 0 :(得分:1)
嗯,错误的格式说明符会产生一些问题。正如Vlad指出的那样,%zu
的结果使用sizeof
很重要,因为在某些系统(64位?)上可能不是int
。如果对printf使用多个参数,这些事情很重要,因为堆栈布局不是printf所期望的。即使使用错误的整数长度转换说明符,单个小值也可能在小端系统上打印正常,因为第一个参数的位置是已知的,而较低有效位的字节首先出现。
但是,你的评论中的一个问题可以很容易地回答:
为什么[0] +1和&amp; a + 1?
之间存在差异
原因是a[0]
是一个5 int
s的数组(矩阵是3个一维数组的序列,每个数组包含5个整数)。它衰减到指向int的指针。向该指针添加一个将其前进到下一个int,这可能是通过将4添加到其数值。
相比之下,&a
不是数组或矩阵,而是已经是地址 - 整个矩阵的地址。数值与&(a[0])
或&(a[0][0])
相同;但它的类型不同。它确实是指向整个矩阵作为单个对象的指针。 (这种情况很少发生,但完全明确且合法。)
矩阵&#39;大小是15个整数,也许是60个字节。在指针中添加一个指针可以将其提升为&#34;下一个矩阵&#34;,可能通过在数字上添加60到地址。
答案 1 :(得分:1)
以下是指针运算的简要教程,因为它对理解事物至关重要。建议您尝试一些事情。
P+n
,其中P
是指针而n
是某个整数值,导致n*sizeof(*P)
字节超过P
。结果的类型与P
的类型相同。这是指针运算的最基本情况。A+n
,其中A
是一个数组,n
是一个整数值,导致n+sizeof(A[0])
字节超过A
。结果的类型是指向A[0]
类型的指针。或者,您可以说A+n
与&A[0]+n
相同。这被称为&#34;阵列衰减&#34;因为表达式A
&#34;衰变&#34;指向其第一个元素的指针(衰减到&A[0]
)。例如,如果A
的类型为int[20][40]
,则它会衰减为int(*)[40]
类型。现在我们可以继续。
sizeof(a)=15
sizeof(a)
结果为15,因为有3 * 5 = 15个char
类型的项目,sizeof(char)
为1.简单地说,sizeof(array) == count*sizeof(type)
。对于多维数组,您只需将计数相乘并创建一个大数,从而生成sizeof(a) == (3*5)*sizeof(char)
或sizeof(a) == (15)*1
。
sizeof(&a)=8
&a
的类型为char (*)[3][5]
,或者&#34;指向3个5个字符的数组&#34;。因为它是一个指针,它将是你机器上指针大小的任何东西。在这种情况下,它是8(这意味着你可能在64位平台上运行它。)
sizeof(a[0])=5
a
是包含5个字符的3个数组,因此a[0]
获得第一个包含5个字符的数组。如前所述,sizeof(array) == count*sizeof(type)
,sizeof(a[0]) == 5*sizeof(char)
或5。
a+1=0x7ffc7d4dfc15
请记住,指针算法强制数组衰减到指向其第一个元素的指针。因此,将a
重写为&a[0]
。现在将1添加到该指针,这将向地址添加1*sizeof(a[0])
或5。如果从打印结果中减去5,则显示a
本身的地址:0x7ffc7d4dfc10
。这对于本答复的其余部分非常重要。
a[0]+1=0x7ffc7d4dfc11
如前所述,a
从0x7ffc7d4dfc10
开始,a[0]
获得第一个包含5个字符的数组。由于a[0]
的类型为char[5]
并且应用了指针运算,因此会发生数组衰减。基本上,您现在拥有&a[0][0]
(类型:char *
)而不是a[0]
。这简单易懂:0x7ffc7d4dfc10 + 1*sizeof(a[0][0])
或0x7ffc7d4dfc10 + 1*1 = 0x7ffc7d4dfc11
。
&a+1=0x7ffc7d4dfc1f
同样,&
检索指向a
的指针。 a
具有类型char[3][5]
(3个5个字符的数组),因此&a
具有类型char(*)[3][5]
(指向3个5个字符数组的指针)。将1添加到此结果为0x7ffc7d4dfc10 + 1*sizeof(char[3][5]) = 0x7ffc7d4dfc10 + (3*5)*sizeof(char) = 0x7ffc7d4dfc10 + 15
。十六进制的15是0x0f,所以0x7ffc7d4dfc10 + 0x0f = 0x7ffc7d4dfc1f
。
&a[0]+1=0x7ffc7d4dfc15
a[0]
的类型为char[5]
。 &a[0]
的类型为char(*)[5]
。在此指针中添加1会导致添加1*sizeof(a[0])
,即5,从而生成0x7ffc7d4dfc10 + 5 = 0x7ffc7d4dfc15
。
答案 2 :(得分:0)
好的,这是我的解释版本。理解多个数组中的地址的关键是一些规则:
规则1 :
如果p是指向数组某个元素的指针,则p ++将p递增以指向下一个元素.. (K&amp; R sec 5.4)和如果是表达式或子表达式是“T的数组”,对于某些T,则表达式的值是指向数组中第一个对象的指针,表达式的类型被更改为指向T的指针。(K&amp; ; R,sec A7.1)
总之,这些暗示如果有一个数组:
T array[arraysize];
对于某些类型T,则数组变为(有一些例外)
T *ptr;
和(array + 1)=(ptr + 1)= ptr_value + 1 x sizeof(T)。 如果这没有意义,假设有一个
char str[] = "hello!";
然后str + 1 = str_addr + 1 x sizeof(char)= str_addr + 1,它指向字母'e'。
规则2:
在C中,二维数组实际上是一维数组,其每个元素都是一个数组(K&amp; R,sec 5.7)
规则3:
当sizeof应用于数组时,结果是数组中的总字节数(K&amp; R,sec A7.4.8)。请注意,这是上述规则1中提到的例外情况之一。
规则4:
对于任何类型的数组:
T array[arraysize];
&amp;数组的类型为T(*)[size]。规则1告诉我们&amp; array + 1将是(array_addr + arraysize)。 (在K&amp; R中没有很好地解释,我认为这是理解这些问题的混乱的根本原因)。
现在让我们来看看原始问题。假设数组a []从内存位置A开始(在本例中为0x7ffc7d4dfc10)。
规则2告诉我们a []实际上是一个包含3个元素的单个数组,每个元素都是一个大小为5的数组。 假装有一个:
typedef char char5_t[5];
然后a []可以表示为:
char5_t a[3];
因此使用规则3,sizeof(a)= 3 x sizeof(char5_t)= 3 x 5 = 15.
sizeof(&amp; a)是指针的大小= 8(64位),因为我在64位机器上运行它。
sizeof(a [0])只是char5_t = 5的大小。您还可以应用规则3来获取sizeof(char [5])= 5.
要计算(a + 1),记住[]可以改写为char5_t a [3]。 规则1告诉我们a + 1 = A + 1 x sizeof(char5_t)= A + 5 = 0x7ffc7d4dfc15。
要计算[0] + 1,想象一下有一个 char5_t a0 = a [0]; 由于a0的实际类型是char [5],因此规则1告诉我们(a0 + 1)= A + 1 * sizeof(char)= 0x7ffc7d4dfc11。
&amp; a + 1由规则4提供,是A + 15 = 0x7ffc7d4dfc10 + 15 = 0x7ffc7d4dfc1f。
&amp; a [0]相当于指向char5_t的指针。使用规则1,&amp; a [0] + 1 = A + 1 * sizeof(char5_t)=
A + 5 = 0x7ffc7d4dfc15。