考虑以下小例子代码:
#include <stdio.h>
#include <stdlib.h>
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 *)
强制转换等等。它们都生成相同的值,但编译器只接受如果使用的演员表是指针的类型,则没有警告。
我真的想知道编译器为什么要求该演员表,即使结果总是相同的。
答案 0 :(得分:33)
使用时:
c2 = i;
编译器警告您将int*
类型分配给char*
。这可能是一个无意的错误。编译器警告你,如果它确实是一个无意的错误,你有机会修复它。
使用时:
c2 = (char *)i;
你告诉编译器你作为程序员知道你在做什么。
答案 1 :(得分:17)
您的计划无效;当指向的类型是不兼容的类型时,不允许在指针类型之间进行赋值,除非使用了强制转换。
6.5.4投射算子
3 - 涉及指针的转换,除了约束条件允许的情况 6.5.16.1,应通过明确的演员表明。
编译器正在接受您的代码作为扩展,但是另一个编译器可能拒绝您的代码并且这样做是正确的。这就是编译器在省略强制转换时发出警告的原因。
答案 2 :(得分:9)
指向不同类型对象的指针可能各自的大小和对齐方式不同:
§6.2.5/ 26类型
指向void的指针应具有与a相同的表示和对齐要求 指向字符类型的指针。同样,指向合格或非限定版本的指针 兼容类型应具有相同的表示和对齐要求。所有 指向结构类型的指针应具有相同的表示和对齐要求 彼此相同。所有指向联合类型的指针都应具有相同的表示形式 对齐要求彼此。指向其他类型的指针不必相同 表示或对齐要求。
特别是,sizeof(char *)
的值可能与sizeof(int *)
不同。此外,char *
可能具有与int *
不同的对齐要求。因此,隐式演员会产生警告。这就是C标准包含以下准则的原因:
§6.5.4/ 1
涉及指针的转换,除了允许的指令之外 6.5.16.1的约束应通过a 显性演员。
答案 3 :(得分:6)
编译器只知道将泛型指针void *
转换为任何指针,反之亦然。除了通用指针之外,它还希望在指针赋值之前进行转换以生成兼容类型的指针,并确定赋值是在知情的情况下完成的。
答案 4 :(得分:3)
在更复杂的解决方案中,如果不这样做,可能会出现问题。在这种情况下它不会引起问题,但是当编译器需要按类型处理指向的内存位置时,它可能会将其视为错误。请注意,char长度为1个字节,而int长度为4或8个字节。
答案 5 :(得分:3)
不幸的是,最好的答案是在对问题的评论中!
参见@PieterWitvoet commment:
在C中,类型系统是一个编译时工具&#39;这会阻止你 执行无效操作。不同类型的东西有 他们可以使用不同的操作,编译器正在制作 确定您只使用有效的操作。但有些操作意味着 一种类型可能适用于另一种类型,所以当你想使用 那些你可以执行类型转换,它基本上告诉编译器: &#34;我知道我在做什么,只是假装这里的东西属于 键入对此操作有效的&#34;。
如果你还没有明白这一点: C是一种静态类型语言。这意味着程序员在使用标识符之前应该定义标识符的类型。因此,C编译器的目的之一是确保为所有标识符分配了一个类型。另一个目的是确保分配的值与分配给它们的标识符的类型相同。在指针的情况下,它们在内部是相同类型的,即它们在32位系统上是32位的。然而,指针的高级方面是它们指向某种类型的指针。数据的。因此,确保为指针分配具有预期类型的数据的地址是有意义的。我依稀记得这样的检查最初并不存在,并且在C标准的后续版本中添加了。但是,我可能是错的。如果没有这样的检查,则可能最终存储错误数据的地址,这将实现为运行时错误。例如,将结构的地址分配给int / char /任何其他类型,然后指向相同的结构,假设它是相关的结构类型,可能会在运行时导致意外问题。并不是这样的赋值是无效的,因此,编译器会警告你,如果你输入值,那么编译器就不会担心它,因为它会让你知道你在做什么。