我无法理解这个程序的输出。 我得到的是,首先,指针p,q,r,s指向null。
然后,有一个类型转换。但是怎么样,输出结果是1 4 4 8。我的想法可能非常错误。所以,如果我错了,请纠正我。
int main()
{
int a, b, c, d;
char* p = (char*)0;
int *q = (int *)0;
float* r = (float*)0;
double* s = (double*)0;
a = (int)(p + 1);
b = (int)(q + 1);
c = (int)(r + 1);
d = (int)(s + 1);
printf("%d %d %d %d\n", a, b, c, d);
_getch();
return 0;
}
答案 0 :(得分:3)
指针算术,在这种情况下,将一个整数值添加到指针值,以指向它的类型为单位前进指针值。如果你有一个指向8字节类型的指针,那么向该指针添加1将使指针前进8个字节。
只有当原始指针和相加的结果都指向同一个数组对象的元素,或者刚好超过它的末尾时,指针算法才有效。
C标准描述的方式是(N1570 6.5.6第8段):
添加或减去具有整数类型的表达式时 从指针开始,结果具有指针操作数的类型。如果 指针操作数指向数组对象的元素和数组 足够大,结果指向一个偏离的元素 原始元素使得下标的差异 结果和原始数组元素等于整数表达式 [...]
如果指针操作数和结果都指向了元素 相同的数组对象,或者超过数组对象的最后一个元素, 评估不得产生溢流;否则,行为 是未定的。如果结果指向一个过去的最后一个元素 数组对象,不得用作一元*
的操作数 被评估的运算符。
刚好超过数组末尾的指针有效,但您无法取消引用它。单个非数组对象被视为1元素数组。
您的程序有未定义的行为。您将1
添加到空指针。由于空指针不指向任何对象,因此对它的指针运算是未定义的。
但编译器并不需要检测未定义的行为,并且您的程序可能处理空指针就像任何有效的指针值一样,并以相同的方式对其执行算术运算。因此,如果空指针指向地址0
(这是不保证,BTW,但它非常常见),那么添加1
将可能给你一个指向地址 N 的指针,其中 N 是它指向的类型的字节大小。
然后将结果指针转换为int
(最好是实现定义,如果指针大于int
则会丢失信息,并且可能会产生陷阱表示)并打印int
值。在大多数系统上,结果可能会显示char
,int
,float
和double
的大小,通常为1,4,4和8字节,分别为。
您的程序行为未定义,但它在您的系统上的实际行为方式是典型的,并不令人惊讶。
这是没有具有未定义行为的程序,它说明了同一点:
#include <stdio.h>
int main(void) {
char c;
int i;
float f;
double d;
char *p = &c;
int *q = &i;
float *r = &f;
double *s = &d;
printf("char: %p --> %p\n", (void*)p, (void*)(p + 1));
printf("int: %p --> %p\n", (void*)q, (void*)(q + 1));
printf("float: %p --> %p\n", (void*)r, (void*)(r + 1));
printf("double: %p --> %p\n", (void*)s, (void*)(s + 1));
return 0;
}
和我系统上的输出:
char: 0x7fffa67dc84f --> 0x7fffa67dc850
int: 0x7fffa67dc850 --> 0x7fffa67dc854
float: 0x7fffa67dc854 --> 0x7fffa67dc858
double: 0x7fffa67dc858 --> 0x7fffa67dc860
输出并不像程序的输出那么清晰,但如果仔细检查结果,可以看到向char*
添加1会使其提前1个字节,int*
或float*
乘4个字节,double*
乘8个字节。 (除了char
,根据定义,它的大小为1个字节,这些在某些系统上可能会有所不同。)
请注意,"%p"
格式的输出是实现定义的,可能会也可能不会反映您可能期望的算术关系类型。我曾在系统(Cray矢量计算机)上工作,其中递增char*
指针实际上会更新存储在64位字的高3位中的字节偏移量。在这样的系统上,除非你知道机器和编译器如何工作的低级细节,否则我的程序(以及你的程序)的输出将更难解释。
但是对于大多数用途,您不需要来了解这些底层细节。重要的是指针算法的工作原理与C标准中描述的一样。了解它在位级上的完成情况对于调试很有用(这几乎是%p
的用途),但不一定要编写正确的代码。
答案 1 :(得分:1)
向指针添加1会将指针前进到适合指针类型的下一个地址。
当(null)指针+ 1重铸为int时,您实际上是在打印指针所指向的每种类型的大小。
printf("%d %d %d %d\n", sizeof(char), sizeof(int), sizeof(float), sizeof(double) );
几乎完全相同。如果你想只用1 BYTE 递增每个指针,你需要在递增它们之前将它们转换为(char *)以让编译器知道
搜索有关指针算术的信息以了解更多信息。
答案 2 :(得分:0)
您需要对指向原始数据类型的指针进行类型转换,而不是将它们类型转换为指针本身,然后使用*
(间接)运算符间接指向该变量值。例如,(int)(p + 1);
表示p
;在这种情况下,指向常量的指针首先递增到内存中的下一个地址(0x1
)。并且此0x1
被强制转换为int
。这完全有道理。
答案 3 :(得分:-1)
您获得的输出与每种相关类型的大小有关。当你这样做指针运算时,它会通过添加的值乘以基本类型大小来增加指针的值。这样做是为了便于正确访问数组。
由于您的计算机上char
,int
,float
和double
的大小分别为1,4,4和8,因此当您为每个关联指针添加1。
编辑:
删除了我认为没有表现出未定义行为的备用代码,实际上已经这样做了。