转换指针 – 运行时有什么区别?

请考虑以下小示例代码:

#include  #include  int main() { int *i; char *c1, *c2; i = malloc(4); *i = 65535; c1 = i; c2 = (char *)i; printf("%p %p %p\n", i, c1, c2); printf("%d %d", *c1, *c2); free(i); return 0; } 

在这个例子中,我分配内存来存储一个整数,由i指出。 然后,我将值65535(1111 1111 1111 1111)存储在*i 。 我接下来要做的是使两个char *指针也指向整数。 我做了两次,但是以两种不同的方式: c1 = i;c2 = (char *)i; 。 最后,我在屏幕上打印所有指针和所有值。 三个指针指向相同的地址,两个值*c1*c2是正确的(-1)

但是,编译器会在此行中生成警告: c1 = i; 。 生成警告是因为我没有使用(char *)强制转换进行赋值。

我想问的是为什么编译器会生成此警告,因为我认为使用c1 = i没有任何区别 ; 或c2 = (char *)i; 。 在这两种情况下,结果都是具有相同大小(以字节为单位)的相同地址。 这对所有强制转换都有效,即使它是(int *)强制转换, (float *)强制转换, (short *)强制转换等等。它们都生成相同的值,但编译器只接受它而不是如果正在使用的强制转换是指针的类型,则发出警告。

我真的想知道为什么编译器要求该转换,即使结果总是相同的。

当你使用:

 c2 = i; 

编译器警告你将int*类型赋值给char* 。 这可能是一个无意的错误。 编译器警告你,如果它确实是一个无意的错误,你有机会修复它。

当你使用:

 c2 = (char *)i; 

你告诉编译器你作为程序员知道你在做什么。

你的程序无效; 除非使用强制转换,否则当指向的类型是不兼容的类型时,不允许在指针类型之间进行赋值。

6.5.4铸造操作员

3 – 除了6.5.16.1的约束允许之外,涉及指针的转换应通过显式转换来指定。

编译器接受您的代码作为扩展 ,但另一个编译器可能拒绝您的代码,这样做是正确的。 这就是编译器在省略强制转换时发出警告的原因。

指向不同类型对象的指针可能在各自的大小和对齐方面有所不同:

§6.2.5/ 26类型

指向void的指针应具有与指向字符类型的指针相同的表示和对齐要求。 同样,指向兼容类型的限定或非限定版本的指针应具有相同的表示和对齐要求。 所有指向结构类型的指针都应具有相同的表示和对齐要求。 所有指向union类型的指针都应具有相同的表示和对齐要求。 指向其他类型的指针不需要具有相同的表示或对齐要求。

特别是, sizeof(char *)可能具有与sizeof(int *)不同的值。 此外, char *可能具有与int *不同的对齐要求。 因此,隐式演员会产生警告。 这就是C标准包含以下准则的原因:

§6.5.4/ 1

除了6.5.16.1的约束允许之外,涉及指针的转换应通过显式转换来指定。

编译器只知道将通用指针void *转换为任何指针,反之亦然。 除了通用指针之外,它还希望在指针赋值之前进行转换以生成兼容类型的指针,并确定赋值是在知情的情况下完成的。

在更复杂的解决方案中,如果不这样做,可能会出现问题。 在这种情况下它不会引起问题,但是当编译器需要按类型处理指向的内存位置时,它可能会将其视为错误。 请注意,char长度为1个字节,而int长度为4或8个字节。

不幸的是,最好的答案是在对问题的评论中!

请参阅@PieterWitvoet提交:

在C中,类型系统是一个编译时“工具”,可以阻止您执行无效操作。 不同类型的东西可以使用不同的操作,编译器确保您只使用有效的操作。 但是对于一种类型的某些操作可能对另一种类型可以正常工作,所以当你想使用它们时你可以执行类型转换,这基本上告诉编译器:“我知道我在做什么,只是假装这个东西这是对此操作有效的类型“。

如果您仍未明白这一点:C是一种静态类型语言。 这意味着程序员应该在使用它们之前定义标识符的类型。 因此,C编译器的目的之一是确保为所有标识符分配了类型。 另一个目的是确保分配的值与分配给它们的标识符的类型相同。 在指针的情况下,它们在内部是相同类型的,即,它们在32位系统上是32位的。 但是,指针的高级方面是它们是指向某种“类型”数据的指针。 因此,确保为指针分配具有预期类型的​​数据的地址是有意义的。 我依稀记得这样的检查最初并不存在,并且在后来的C标准版本中添加了。 但是,我可能是错的。 如果没有这样的检查,则可能最终存储错误数据的地址,这将实现为运行时错误。 例如,将结构的地址分配给int / char /任何其他类型,然后指向相同的结构,假设它是相关的结构类型,可能会在运行时导致意外问题。 并不是这样的赋值是无效的,因此,编译器会警告你,如果你输入值,那么编译器就不会担心它,因为它会让你知道你在做什么。