首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >转换函数指针无效的效果是什么?

转换函数指针无效的效果是什么?
EN

Stack Overflow用户
提问于 2012-04-24 20:02:41
回答 4查看 856关注 0票数 0

所以我第64次尝试写一个缓冲库,我开始进入一些相当先进的东西。我想就这件事征求一些意见。

在我的第一个头文件中,我有以下内容:

代码语言:javascript
复制
typedef struct StdBuffer { void* address; } StdBuffer;
extern void StdBufferClear(StdBuffer);

在第一个头文件#includes的另一个头文件中,我有以下内容:

代码语言:javascript
复制
typedef struct CharBuffer { char* address; } CharBuffer;
void (*CharBufferClear)(CharBuffer) = (void*) StdBufferClear;

声明此函数指针无效会干扰调用吗?它们通过值签名进行匹配。我以前从未见过函数指针声明为空,但这是使其编译干净的唯一方法。

就堆栈而言,它与我在汇编程序编码中学到的内容完全没有任何区别。

无关的 OMG!我刚在StackOverflow上说了Stackwise!

嗯..。看来我在这里想得太多了。如果可以的话,请允许我再次澄清。我不在乎在地址上存储什么类型的数据。我所关心的只是“单位”的大小,以及地址上有多少单位。如果您愿意的话,可以查看API的接口协议契约:

代码语言:javascript
复制
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之类的人并不在乎地址里有什么东西,他们只想知道我在这里清楚地注意到的细节。

现在来看一下遵循此合同的第一个原型:

代码语言:javascript
复制
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就是addressbytebytelonglong

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

具有使用代码执行自己的强制转换和使用api函数操作数据的能力,这些函数对具有特定长度内存单元的特定长度内存块进行操作。但是,原型必须包含正式的数据指针类型,因为最终用户每次想要使用该地址指针进行操作时都必须重铸他们的数据,这是没有意义的。如果指针为空,那么称它为CharBuffer是没有意义的。

StdBuffer是一种泛型类型,除了api本身之外,从来没有使用过它来管理所有遵守合同的数据类型。

这个系统将包含的api来自我最新版本的缓冲。这里非常清楚地说明了这一点,@Google代码,我知道,如果没有大量的研究和意见收集,我将无法安全地从api中直接操作数据,这将需要一些东西来改变这一切。

这使我注意到,我还需要在StdBufferFlags成员中设置一个签名/未签名的位标志。

也许这个谜题的最后一部分也是为了让你细读。

代码语言:javascript
复制
/** \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;
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-04-24 20:09:17

此代码需要诊断:

代码语言:javascript
复制
void (*CharBufferClear)(CharBuffer) = (void*) StdBufferClear;

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

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

代码语言:javascript
复制
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的任务覆盖。这是一个无效混叠的例子。

票数 3
EN

Stack Overflow用户

发布于 2012-04-24 20:11:53

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

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

票数 1
EN

Stack Overflow用户

发布于 2013-01-09 16:41:48

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10305192

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档