为什么第二个printf输出0?

3
#include<stdio.h>
int main()
{
    char arr[] = "somestring";
    char *ptr1 = arr;
    char *ptr2 = ptr1 + 3;
    printf("ptr2 - ptr1 = %ld\n", ptr2 - ptr1);
    printf("(int*)ptr2 - (int*) ptr1 = %ld",  (int*)ptr2 - (int*)ptr1);
    return 0;
}

我明白。

我了解

 ptr2 - ptr1

代码输出3,但是无法理解为什么第二个printf输出0。


1
提问还会被踩? - Ian McGrath
1
@Jerry,不是我写的。这是我做的一个C语言测验。我答错了。 - Ian McGrath
1
尝试调试并检查值(观察)。 - Jerry
1
根据指针数学,当你加上 1 时,结果将指向相同类型的下一个位置。假设 arr == 12,那么 ptr1 == 12,然后 ptr1 + 3 == 15,所以 ptr2 == 15。现在,如果你在添加 + 之前将 ptr1 转换为 int*,则 (int*)ptr1 + 1 = 16,对于 sizeof (int*) == 4 的系统而言。 - Grijesh Chauhan
2
这是否属于未定义行为(UB)?因为(int*)ptr1没有正确对齐(取决于平台),所以对其进行取消引用可能会出现问题。但是,如果指针对齐不正确,甚至减法本身也可能是UB吗? - Aaron McDaid
显示剩余4条评论
4个回答

8

这是因为当你从两个指针中减去时,你得到的是指针之间的距离,以元素数量而不是字节数来表示。

(char*)ptr2-(char*)ptr1  // distance is 3*sizeof(char), ie 3
(int*)ptr2-(int*)ptr1  // distance is 0.75*sizeof(int), rounded to 0 

编辑:我之前说过的强制转换指针对齐的说法是错误的。


1
我的机器上 int* 的大小是8字节。但是 ptr2=ptr1+5 却得到了1。所以我猜即使大小为8字节,它也可以在4字节边界上对齐。我是对的吗? - Ian McGrath
@IanMcGrath sizeof(int) 是4,而不是sizeof(int*) - Yu Hao
@BenoitBlanchon,现在它变得非常清晰了。谢谢。 - Ian McGrath
抱歉,我走得太快了。 - Benoit Blanchon

4
如果您想检查地址之间的距离,请勿使用(int *)(void *)ptrdiff_t 是一种能够表示任何有效指针减法操作结果的数据类型。
#include <stdio.h>
#include <stddef.h>

int main(void)
{
    char arr[] = "somestring";
    char *ptr1 = arr;
    char *ptr2 = ptr1 + 3;
    ptrdiff_t diff = ptr2 - ptr1;

    printf ("ptr2 - ptr1 = %td\n", diff);
    return 0;
}

编辑:正如@chux指出的那样,使用"%td"字符来表示ptrdiff_t


1
不知道这一点。谢谢你的回答,我接受了Benoit Blanchon的回复,因为他解释了为什么是0。你提到的内容仍然很有用。+1 - Ian McGrath
1
根据C11标准6.5.6 9,差异在于类型ptrdiff_t。因此将差异转换为(ptrdiff_t)类型不会改变任何内容。要打印此类型,应使用t修饰符,如printf("%td\n", ptr2 - ptr1) - chux - Reinstate Monica

1
char 指针强制转换为 int* 可以使其对齐到 4 字节(假设这里的 int 是 4 字节)。尽管 ptr1ptr2 相隔 3 字节,但将它们强制转换为 int* 后,结果相同,因此得出了这个结果。

0

这是因为 sizeof(int) == 4

每个字符占用1个字节。在内存中,您的字符数组看起来像这样:

[s][o][m][e][s][t][r][i][n][g][0]

当你有一个整数数组时,每个整数占用四个字节。存储'1'和'2'的概念上看起来更像这样:

[0][0][0][1][0][0][0][2]

因此,Ints必须对齐到4字节边界。您的编译器将地址别名到最低整数边界。如果您使用4而不是3,则会注意到它按预期工作。

您必须执行减法才能使其执行(仅将转换后的指针传递给printf无法执行),因为printf不是严格类型化的,即%ld格式不包含参数是int指针的信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接