我有这两个陈述:
printf("%u",a+1);
和
printf("%u",(int *)a+1);
实际上,当我遇到这种混乱时,我正在研究这段代码。
#include<stdio.h>
int main()
{
int a[2][2]={1,2,3,4};
int i,j;
int *p[] = { (int*)a, (int*)a+1, (int*)a+2 };
for(i=0; i<2; i++){
for(j=0; j<2; j++){
printf("%d %d %d %d",* (*(p+i)+j), *(*(j+p)+i), *(*(i+p)+j), *(*(p+j)+i));
}
}
return 0;
}
Output:
1 1 1 1
2 2 2 2
2 2 2 2
3 3 3 3
为了理解上述程序的输出,我开始知道如果我知道上述两个语句之间的区别,那么输出这个输出的差异就可以解决了。
我目前的理解:
(a+1)
会给我数组的第二个元素的地址。在这种情况下,可以将2维阵列可视化为2个1维阵列,每个阵列具有2个元素。因此(a+1)
会向我提供a[1][0]
的地址,但为什么(int *)a+1
会向我提供a[0][1]
的地址?
请解释程序的差异和输出。
感谢。
答案 0 :(得分:3)
习语(int*)a+1
被解释为((int*)a) + 1)
。也就是说,演员阵容优先于加法。所以这个求值为(int*) a)
,这是数组的地址为ptr-to-int,偏移量为1,返回数组中的第二个元素(2
)。
两个关键的编程规则:
规则1:当您编写代码时,请使布局反映功能。
规则2:当您读取代码时,请阅读功能,而不是布局。 (推论:调试代码,而不是注释。)
从概念上讲,当你宣布
时int a[2][2]={1,2,3,4};
你想象一个像这样的二维数组:
1 2
3 4
但是C实际上将数据存储在一个连续的内存块中,如下所示:
1 2 3 4
它“记住”数据在计算索引时表示2×2数组。但是当你将a
从其原始类型转换为int *
时,你告诉编译器忘记它的原始声明,实际上失去了它的二维性并成为int
的简单向量第
以下是如何理解p
的声明:
int *p[] = { (int*) a, (int*) a+1, (int*) a+2 }; // As written
int *p[] = { (int*) a, ((int*) a) + 1, ((int*) a) + 2 }; // As interpreted
int *p[] = { &a[0][0], &a[0][1], &a[1][0] }; // Resulting values
由此可以看出p
是一维向量数组:
p[0] = { 1, 2, 3 }
p[1] = { 2, 3 }
p[2] = { 3 }
如果您认出(p+i) == (i+p)
,那么最后两项与该行中的前两项相同
printf("%d %d %d %d\n",* (*(p+i)+j), *(*(j+p)+i), *(*(i+p)+j), *(*(p+j)+i));
相当于:
printf("%d %d %d %d\n", p[i+j], p[j+i], p[i+j], p[j+i]);
值得注意的是,由于以下内容都是等效的:
a[i]
*(a+i)
*(i+a)
然后写i[a]
来表示相同的值是完全合法的。换句话说,编译器允许您编写
printf("%d %d %d %d\n", p[i], i[p], p[1], 1[p]);
当然,您的技术主管最好不允许您编写。如果你在我的小组中写下这个,那你就被解雇了。 ; - )
答案 1 :(得分:2)
无,都产生未定义的行为。打印指针值的正确格式为%p
。将指针发送到void*
时,将指针投向printf
。
答案 2 :(得分:0)
多维C数组的行为类似于彼此相邻的单维数组。因此,如果您将a
(通常为(int **)
)投射到(int *)
,您将获得与a[0]
相同的内容。因此(int *) a + 1
是&a[0][1]
。否则,您的理解是正确的,这就是a + 1
为您提供&a[1][0]
。
(此行为由标准指定;但这并不能使其成为良好的编程习惯。)