你能不能假设类型转换指针是安全的?

时间:2013-12-05 14:38:18

标签: c function pointers casting sizeof

我从很多人那里听说你无法保证类型转换会无损地执行。只有在您不了解处理器时才会这样,也就是说,您还没有验证用于数据类型的字节数?让我举个例子:

如果执行以下操作:

typedef struct
{
    int i;
    char c;
    float f;
    double d;
} structure;

size_t voidPtrSz = sizeof(void *);
size_t charPtrSz = sizeof(char *);
size_t intPtrSz = sizeof(char *);
size_t floatPtrSz = sizeof(float *);
size_t doublePtrSz = sizeof(double *);
size_t structPtrSz = sizeof(structure *);
size_t funcPtrSz = sizeof(int (*)(float, char));

printf("%lu\n", voidPtrSz);
printf("%lu\n", charPtrSz);
printf("%lu\n", intPtrSz);
printf("%lu\n", floatPtrSz);
printf("%lu\n", doublePtrSz);
printf("%lu\n", structPtrSz);
printf("%lu\n", funcPtrSz);

...输出如下......

4
4
4
4
4
4
4

您能否假设在所有情况下都可以安全地将特定数据类型指针类型转换为另一个数据类型指针?例如,如果执行此操作:

int foo(float, char)
{
}

void *bar(void)
{
    return (void *)foo;
}

int (*pFunc)(float, char) = bar();

您能否确定pFunc的地址为foo

3 个答案:

答案 0 :(得分:6)

关于您的特定代码示例,让我们参考C99语言标准的第6.3.2.3节:

  

指向void的指针可以转换为指向任何不完整或对象类型的指针。指向任何不完整或对象类型的指针可以转换为指向void并再次返回的指针;结果应该等于原始指针。

请注意,指向函数的指针与指向对象的指针不同。唯一提到指针到函数的转换是:

  

指向一种类型的函数的指针可以转换为指向另一种类型的函数的指针,然后再返回;结果应该等于原始指针。如果转换的指针用于调用类型与指向类型不兼容的函数,则行为未定义。

所以你的代码示例调用了未定义的行为。

如果我们避免使用函数指针转换,则以下段落将解释所有内容:

  

指向对象或不完整类型的指针可能会转换为指向不同对象或不完整类型的指针。如果生成的指针未针对指向类型正确对齐,则行为未定义。否则,当再次转换回来时,结果将与原始指针进行比较。

注意:转换指针类型与转换和然后解除引用是一个单独的问题(通常,只有在您转换为char *时才有效然后解除引用。)

答案 1 :(得分:2)

  

您能否假设在所有情况下都可以安全地将特定数据类型指针类型转换为另一个数据类型指针?

任何数据指针都可以安全地转换为char*void*。这样创建的任何char*void*都可以转换回其原始类型。当对指针执行间接寻址时,任何其他数据指针转换都会导致未定义的行为。

任何函数指针类型都可以强制转换为任何其他函数指针类型,但不应该通过错误的类型调用函数。将函数指针强制转换为void*或任何其他数据指针类型会导致未定义的行为。

  

只有在您不了解处理器时才会这样,也就是说,您还没有验证用于数据类型的字节数?

即便如此,你也不安全。当C标准表示构造具有未定义的行为时,编译器编写者可以根据需要自由地处理构造。结果是,即使您,您知道将使用UB构造因为您了解目标CPU,优化编译器可能会削减角落并生成与您预期的完全不同的代码。

答案 2 :(得分:1)

@Oli Charlesworth给你一个很好的答案。

我希望我能对指针是什么有所了解,这样你就可以更好地理解指针机制:

指针是一个地址。该地址是数据第一个字节的地址。指针的类型指定从第一个字节开始的字节数是数据的一部分以及这些字节如何编码数据。

例如,在gcc x86上,如果你有一个int * p,p所持有的值告诉数据的起始地址,而p(int *)的类型告诉那个地址他将以二进制补码有符号数表示来解释4个字节(以小端字节顺序排列)。

void *指针是“通用指针”。指针仍然保存一个地址,但指针类型没有指定你在那里找到什么类型的数据,甚至没有指定数据形成多少字节,所以你永远不能通过void *指针访问数据,但是作为回答之前,您可以安全地在指向void的指针和指向任何不完整或对象类型的指针之间进行转换。

指向函数的指针保存函数的地址,指针的类型告诉如何调用该函数(什么参数和什么类型)以及函数返回的内容。