c - 对指向 int 的指针进行类型转换。

标签 c pointers

我无法理解这个程序的输出。 我得到的是,首先,指针 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; 
}

最佳答案

指针运算,在这种情况下,将一个整数值加到一个指针值上,指针值以它指向的类型为单位递增。如果您有一个指向 8 字节类型的指针,则向该指针加 1 将使指针前进 8 个字节。

仅当原始指针和加法结果都指向同一个数组对象的元素或刚好超过它的末尾时,指针算法才有效。

C 标准对此的描述方式是(N1570 6.5.6 第 8 段):

When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression.
[...]
If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.

刚好超过数组末尾的指针是有效的,但您不能取消引用它。单个非数组对象被视为 1 元素数组。

您的程序有未定义的行为。您将 1 添加到空指针。由于空指针不指向任何对象,因此其上的指针算法未定义。

但是编译器不需要检测未定义的行为,并且您的程序可能像对待任何有效指针值一样对待空指针,并以相同的方式对其执行算术运算。因此,如果空指针指向地址 0(这是不能保证的,顺便说一句,但这很常见),然后向其添加 1可能给你一个指向地址 N 的指针,其中 N 是它指向的类型的大小(以字节为单位)。

然后,您将结果指针转换为 int(这最多是实现定义的,如果指针大于 int,将会丢失信息,并且可能产生 trap representation) 然后打印 int 值。在大多数系统上,结果可能会显示 charintfloatdouble 的大小,通常分别为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

输出不像程序的输出那么清晰,但如果仔细检查结果,您会发现将 1 添加到 char* 会使它前进 1 个字节,即 int* float* 4 个字节,double* 8 个字节。 (除了 char,根据定义其大小为 1 个字节,这些在某些系统上可能会有所不同。)

请注意,"%p" 格式的输出是实现定义的,可能反射(reflect)也可能不反射(reflect)您可能期望的那种算术关系。我曾在系统(Cray vector 计算机)上工作过,其中递增 char* 指针实际上会更新存储在 64 位字的高 3 位中的字节偏移量。在这样的系统上,我的程序(和你的)的输出将更难以解释,除非你知道机器和编译器如何工作的底层细节。

但对于大多数用途,您不需要了解那些底层细节。重要的是指针算法按照 C 标准中的描述工作。了解它是如何在位级别上完成的对于调试很有用(这几乎就是 %p 的用途),但对于编写正确的代码并不是必需的。

关于c - 对指向 int 的指针进行类型转换。,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31883979/

相关文章:

c - C中结构成员(指针与数组)的内存分配之间的差异

c++ - 如何在方法中传递智能指针而不删除它

c# - 在 .NET 4.0 之前的 C# 中,是否有一种安全的方法来执行指针运算?

c - UART 缓冲区未在 PIC24F 上引发标志

c - 如何使用指针将字符串分割成两个字符串? C语言

c - 在 Visual Studio 2017 中导入 libssh 库

编译器不给出错误 undefined reference 的行号

pointers - 使用指针接收器方法创建包装现有类型的接口(interface)

c - C中重叠对象的语义是什么?

c - 寻找从魔数(Magic Number)确定文件类型