将函数指针转换为void会产生什么影响?

时间:2012-04-24 20:02:41

标签: c struct function-pointers void-pointers function-prototypes

所以我试图第64次写一个缓冲库,我开始进入一些非常先进的东西。以为我会就此提出一些专业意见。

在我的第一个头文件中,我有这个:

typedef struct StdBuffer { void* address; } StdBuffer;
extern void StdBufferClear(StdBuffer);

在另一个头文件#includes第一个头文件中我有这个:

typedef struct CharBuffer { char* address; } CharBuffer;
void (*CharBufferClear)(CharBuffer) = (void*) StdBufferClear;

声明此函数指针void是否会干扰调用?它们通过值签名进行匹配。我之前从未见过一个声明为void的函数指针,但它是让它干净利地编译的唯一方法。

Stackwise它与我在汇编编码中学到的东西完全没有任何区别。

无关紧要 OMG!我刚刚在StackOverflow上说Stackwise!

嗯..看起来我在这里假设太多了。请允许我重新说明我是否可以。我不关心地址中存储的“类型”数据。我所关心的只是“单位”的大小以及地址上有多少单位。如果您愿意,请查看API的接口协议合同:

typedef struct StdBuffer {

    size_t width;        ///< The number of bytes that complete a data unit.
    size_t limit;        ///< The maximum number of data units that can be allocated for this buffer.
    void * address;      ///< The memory address for this buffer.
    size_t index;        ///< The current unit position indicator.
    size_t allocated;    ///< The current number of allocated addressable units.
    StdBufferFlags flags;///< The API contract for this buffer.

} StdBuffer;

你看,memcpy,memmove等并不关心地址,他们想要的只是我在这里清楚记录的具体内容。

现在看一下遵循这份合同的第一个原型:

typedef struct CharBuffer {

    size_t width;        ///< The number of bytes that complete a data unit.
    size_t limit;        ///< The maximum number of data units that can be allocated for this buffer.
    char * address;      ///< The memory address for this buffer.
    size_t index;        ///< The current unit position indicator.
    size_t allocated;    ///< The current number of allocated addressable units.
    CharBufferFlags flags;///< The API contract for this buffer.

} CharBuffer;

正如您清楚地看到数据类型在此上下文中无关紧要。您可以说C根据具体情况对其进行不同的处理,但在一天结束时,addressaddressbytebyte且{只要我们在同一台机器上处理内存,{1}}就是long

这个系统汇集在一起​​的目的是删除所有这种基于类型的杂耍C似乎是如此引以为豪(并且理所当然......)它对我想做的事情毫无意义。这是为任何地址的任何标准大小的数据(1,2,4,8,sizeof(RandomStruct))创建一个合同持久原型。

能够使用代码执行我自己的转换,并使用api函数操作该数据,这些函数在具有特定长度内存单元的特定长度内存块上运行。但是,原型必须包含官方数据指针类型,因为对于最终用户每次想要使用该地址指针执行某些操作时必须重新创建数据是没有意义的。如果指针无效,则将其称为CharBuffer是没有意义的。

long是一种通用类型,除了在api本身之外,它永远不会被用来管理所有合同持久数据类型。

该系统将包含的api来自我最新版本的缓冲。这里有一个非常明确的文件记录@Google Code我知道有些事情需要改变以便将所有这些结合在一起,即如果没有大量适当的研究和意见,我将无法安全地直接从api内部操纵数据。聚会。

这引起了我的注意,我还需要StdBufferFlags成员中的有符号/无符号位标志。

也许这个难题的最后一块也是为了你的细读。

StdBuffer

4 个答案:

答案 0 :(得分:3)

此代码需要诊断:

void (*CharBufferClear)(CharBuffer) = (void*) StdBufferClear;

您正在将void *指针转换为没有强制转换的函数指针。在C中,void *指针可以在没有强制转换的情况下转换为指向对象类型的指针,但不能转换为函数指针类型。 (在C ++中,为了增加安全性,还需要强制转换void *到对象类型。)

你想要的只是在函数指针类型之间进行转换,即:

void (*CharBufferClear)(CharBuffer) = (void (*)(CharBuffer)) StdBufferClear;

然后你仍然在进行相同类型的惩罚,因为函数是不同的类型。您正在尝试调用一个函数,该函数使用指向StdBuffer的函数的指针CharBuffer

这种类型的代码没有明确定义C.在打败了类型系统之后,你就独立了,依靠测试,检查目标代码,或者从编译器编写者那里得到一些保证,这种东西是有效的用那个编译器。

您在汇编编码中学到的内容不适用,因为汇编语言只有少量基本数据类型,如“机器地址”或“32位字”。具有相同布局和低级别表示的两个数据结构可能是不兼容类型的概念在汇编语言中不会出现。

即使两个类型在低级别看起来相同(另一个示例:unsigned intunsigned long有时完全相同)C编译器可以根据类型规则没有的假设来优化程序被侵犯了。例如,假设AB指向相同的内存位置。如果您分配给对象A->member,则C编译器可以假定对象B->member不受此影响,如果A->memberB->member具有不兼容的类型,例如是char *而另一个是void *。生成的代码会在寄存器中缓存B->member的旧值,即使内存中的副本被A->member的赋值覆盖了。这是无效别名的示例。

答案 1 :(得分:1)

标准没有定义将函数指针转换为void *的结果。

同样,在函数指针之间进行转换然后通过错误的方法调用也是未定义的行为。

答案 2 :(得分:0)

有一些构造需要符合标准的C编译器一致地实现,并且有一些构造,99%的C编译器确实一致地实现,但符合标准的编译器可以自由地实现不同的构造。尝试将指向一个类型指针的函数的指针转换为指向另一类指针的函数的指针,属于后一类。尽管C标准指定void*char*必须具有相同的大小,但没有什么要求它们共享相同的位级存储格式,更不用说参数传递约定。虽然大多数机器允许以与单词大致相同的方式访问字节,但这种能力并不普遍。应用程序二进制接口的设计者[指定参数如何传递给例程的文档]可能指定以最大化字节访问效率的方式传递char*,而void*应该以最大化字访问效率的方式传递1}},同时保留保持未对齐字节地址的能力,可能通过使用补充字来保持零或一个来指示LSB / MSB)。在这样的机器上,让一个期望从代码调用void*的例程期望通过char*可能导致例程访问任意错误的数据。

答案 3 :(得分:-1)

不,用于存储数据的数据类型无关紧要。只有C类用于读取和写入数据,并且数据足够大。