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

所以我想第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根据具体情况对其进行不同的处理,但在一天结束时,只要我们在同一台机器上处理内存, address就是一个address ,一个byte就是byte而一个long就是一个long整数。 。

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

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

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

该系统将包含的api来自我最新版本的缓冲。 这里有相当清楚的文档@Google Code我知道有些事情需要改变才能将这些全部整合在一起,即如果没有大量适当的研究和意见收集,我将无法安全地直接从api内部操作数据。

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

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

 /** \def BIT(I) \brief A macro for setting a single constant bit. * * This macro sets the bit indicated by I to enabled. * \param I the (1-based) index of the desired bit to set. */ #define BIT(I) (1UL << (I - 1)) /** \enum StdBufferFlags \brief Flags that may be applied to all StdBuffer structures. * These flags determine the contract of operations between the caller * and the StdBuffer API for working with data. Bits 1-4 are for the * API control functions. All other bits are undefined/don't care bits. * * If your application would like to use the don't care bits, it would * be smart not to use bits 5-8, as these may become used by the API * in future revisions of the software. */ typedef enum StdBufferFlags { BUFFER_MALLOCD = BIT(1), ///< The memory address specified by this buffer was allocated by an API BUFFER_WRITEABLE = BIT(2), ///< Permission to modify buffer contents using the API BUFFER_READABLE = BIT(3), ///< Permission to retrieve buffer contents using the API BUFFER_MOVABLE = BIT(4) ///< Permission to resize or otherwise relocate buffer contents using the API }StdBufferFlags; 

此代码需要诊断:

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

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

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

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

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

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

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

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

该标准没有定义将函数指针转换为void *

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

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

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