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

时间:2015-01-05 18:20:23

标签: c pointers casting

考虑以下小例子代码:

#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 *)强制转换等等。它们都生成相同的值,但编译器只接受如果使用的演员表是指针的类型,则没有警告。

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

6 个答案:

答案 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 /任何其他类型,然后指向相同的结构,假设它是相关的结构类型,可能会在运行时导致意外问题。并不是这样的赋值是无效的,因此,编译器会警告你,如果你输入值,那么编译器就不会担心它,因为它会让你知道你在做什么。