这是代码:
#include<stdio.h>
#include<conio.h>
int main()
{
int *p1,*p2;
int m=2,n=3;
m=p2-p1;
printf("\np2=%u",p2);
printf("\np1=%u",p1);
printf("\nm=%d",m);
getch();
return 0;
}
这将输出为:
p2= 2686792
p1= 1993645620
m= -497739707
我对代码和输出有两个疑问:
由于'm'是一个int,它不应该将p2-p1作为输入,因为p1和p2都是指针而m是一个整数,它应该给出一个错误,如“从'int'无效转换'int'“但不是。为什么呢?
即使在输入后,差异也无效。为什么?
答案 0 :(得分:1)
因为'm'是一个int,所以不应该将p2-p1作为输入,因为p1和 p2都是指针,m是一个整数,它应该给出一个错误 “从'int'无效转换为'int'”但事实并非如此。为什么?的
此类错误或警告取决于您使用的编译器。 C编译器经常给程序员提供足够的绳索让自己挂起......
即使在输入后,差异也无效。为什么?
实际上,差异是正确的!它正在使用pointer arithmetic来执行计算。所以对于这个例子..
p2 = 2686792
p1 = 1993645620
由于指针未初始化,因此会为它们分配一些垃圾值,如上所述。现在,您想执行操作p2 - p1,即。你要的是在p2 之前恰好是p1内存块的内存地址。由于p1和p2是整数的指针,因此内存块的大小为sizeof(int)
(几乎总是4个字节)。因此:
p2 - p1 =(2686792 - 1993645620)/ sizeof(int)
=(2686792 - 1993645620)/ 4 = -497739707
答案 1 :(得分:0)
由于'm'是一个int,它不应该把p2-p1作为输入,因为p1和p2都是指针而m是一个整数,它应该给出一个错误,比如“从'int'无效转换为'int “但事实并非如此。为什么呢?
C ++规范声明这是合法的 从C ++ 11规范5.7.6:
当减去指向同一数组对象的元素的两个指针时,结果是两个数组元素的下标的差异。结果的类型是实现定义的有符号整数类型;此类型应与标题(18.2)中定义为std :: ptrdiff_t的类型相同。
......在该段后面......
除非两个指针都指向同一个数组对象的元素,否则 在数组对象的最后一个元素之后,行为未定义。
p2-p1的结果与std :: ptrdiff_t的类型相同,但没有任何说明编译器不能将其定义为
namspace std {
typedef ptrdiff_t int;
}
但是,您也没有得到保证,这将适用于所有平台。例如,某些平台(尤其是64位平台)将使用long for ptrdiff_t。在这些平台上,您的代码将无法编译,因为它依赖于ptrdiff_t的实现定义类型。
关于你的第二个问题,C ++规范5.7.6中的措辞表明了它们的工作方式。措辞表明该语言的编写者需要指针算法来支持快速算术,同时通过数组工作。因此,当在操作数组的上下文中使用时,定义了指针减法的结果以产生方便的结果。您可以构建一种语言,其中两个指针之间的差异是它们的内存地址之间的差异(以字节为单位),并且您将拥有一致的工作语言。然而,一个好的语言总能获得最好的帮助。作者认为,对阵列进行干净的操作比获得字节差异更有价值。例如:
double* findADouble(double* begin, double* end, double valueToSearchFor)
{
for (double* iter = begin; iter != end; iter++) {
if (*iter == valueToSearchFor)
return iter;
}
return 0;
}
必须有一个sizeof,我们必须使用+ =而不是++。
double* findADouble(double* begin, double* end, double valueToSearchFor)
{
for (double* iter = begin; iter != end; iter += sizeof(double)) {
if (*iter == valueToSearchFor)
return iter;
}
return 0;
}
在他们的决定中也值得注意:当在C中创建规则时,优化编译器的工作并不是很好。 iter + = sizeof(double)可以编译成比++ iter低得多的汇编代码,即使这两个操作从根本上做同样的事情。现代优化器对此没有任何问题,但语法仍然存在。