将函数指针强制转换为空有什么作用? [英] What is the effect of casting a function pointer void?

查看:110
本文介绍了将函数指针强制转换为空有什么作用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我试图第64次编写缓冲库,并且开始接触一些非常高级的内容.以为我会要求对此进行一些专业性的投入.

So I'm trying to write a buffering library for the 64th time and I'm starting get into some pretty advanced stuff. Thought I'd ask for some proffesional input on this.

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

In my first header file I have this:

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

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

In another header file that #includes the first header file I have this:

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

将这个函数指针声明为void是否会干扰调用?它们具有按值签名匹配.我以前从未见过将函数指针声明为void,但是这是使其完全编译的唯一方法.

Will declaring this function pointer void interfere with the call? They have matching by value signatures. I have never seen a function pointer declared void before, but its the only way to get it to compile cleanly.

从堆栈上看,它与我在汇编程序编码中学到的没有任何区别.

Stackwise it should not make any difference at all from what I learned in assembler coding.

irrelevent ,天哪!我只是在StackOverflow上说了Stackwise!

irrelevent OMG! I just said Stackwise on StackOverflow!

嗯..看来我在这里承担太多了.请允许我澄清.我不在乎地址中存储了什么类型"的数据.我只关心一个单位"的大小以及地址中有多少个单位.如果您愿意,请查看API的接口协议合同:

Hmm.. Looks like I've assumed too much here. Allow me to reclarify if I may. I don't care what 'type' of data is stored at the address. All that I am concerned with is the size of a 'unit' and how many units are at the address. Take a look at the interface agreement contract for the API if you will:

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之类的东西并不真正在乎某个地址的内容,他们想要的只是我明确要在此处跟踪的细节.

You see, memcpy, memmove and the like don't really care whats at an address all they want is the specifics which I'm clearly keeping track of here.

现在看看遵循该合同的第一个原型:

Have a look now at the first prototype to follow this contract:

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,而longlong只要我们在同一台计算机上处​​理内存.

As you an clearly see the data type is irrelevant in this context. You can say that C handles it differently depending on the case, but at the end of the day, an address is an address, a byte is byte and a long is a long for as long as we are dealing with memory on the same machine.

将这个系统放在一起的目的是消除所有基于类型的杂耍C,它似乎为此感到骄傲(并且理应如此……)对于我想做的事情来说毫无意义.它将为位于任何地址的任何标准大小的数据(1、2、4、8,sizeof(RandomStruct))创建一个遵守合同的原型.

The purpose of this system when brought together is to remove all of this type based juggling C seems to be so proud of (and rightfully so...) Its just pointless for what I would like to do. Which is create a contract abiding prototype for any standard size of data (1, 2, 4, 8, sizeof(RandomStruct)) located at any address.

具有使用代码执行我自己的转换并使用api函数处理该数据的能力,这些函数在具有特定长度存储单元的特定长度存储块上运行.但是,原型必须包含官方的数据指针类型,因为对于最终用户而言,每次他们想使用该地址指针执行某些操作时,最终用户都不必重铸其数据,这是没有意义的.如果指针为空,则将其称为CharBuffer是没有意义的.

Having the ability to perform my own casting with code and manipulate that data with api functions that operate on specific length blocks of memory with specific length memory units. However, the prototype must contain the official data pointer type, because it just doesn't make sense for the end user to have to recast their data every time they would like to do something with that address pointer. It would not make sense to call it a CharBuffer if the pointer was void.

StdBuffer是一种通用类型,除非在api自身内部用于管理所有遵守合同的数据类型,否则永远不会使用.

The StdBuffer is a generic type that is never EVER used except within the api itself, to manage all contract abiding data types.

该系统将包含的api来自我最新的缓冲版本. @Google代码中,我知道需要进行一些更改才能将所有内容整合在一起,也就是说,如果没有很多适当的操作,我将无法直接从api中安全地直接操作数据.研究和意见收集.

The api that this system will incorporate is from my latest edition of buffering. Which is quite clearly documented here @Google Code I am aware that some things will need to change to bring this all together namely I won't have the ability to manipulate data directly from within the api safely without lots of proper research and opinion gathering.

刚刚引起我注意的是,我还需要StdBufferFlags成员中的有符号/无符号位标志.

Which just brought to my attention that I also need a Signed/Unsigned bit flag in the StdBufferFlags Members.

也许这个难题的最后一部分也是为了您的细读.

Perhaps the final piece to this puzzle is also in order for your perusal.

/** \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 *指针转换为不带强制转换的函数指针.在C语言中,void *指针可以转换为指向对象类型的指针而无需强制转换,但是不能转换为函数指针类型. (在C ++中,为了增强安全性,还需要强制转换将void *转换为对象类型.)

You're converting a void * pointer to a function pointer without a cast. In C, a void * pointer can convert to pointers to object types without a cast, but not to function pointer types. (In C++, a cast is needed to convert void * to object types also, for added safety.)

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

What you want here is just to cast between function pointer types, i.e.:

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

然后,由于功能是不同的类型,因此您仍在进行相同类型的修饰.您正在尝试使用指向采用CharBuffer的函数的指针来调用采用StdBuffer的函数.

Then you are still doing the same type punning because the functions are different types. You are trying to call a function which takes a StdBuffer using a pointer to a function which takes a CharBuffer.

这种类型的代码不是定义明确的C.在击败类型系统之后,您自己一个人依靠测试,检查目标代码或从编译器作者那里获得某种保证,即这种方法是可行的那个编译器.

This type of code is not well-defined C. Having defeated the type system, you're on your own, relying on testing, examining the object code, or obtaining some assurances from the compiler writers that this sort of thing works with that compiler.

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

What you learned in assembler coding doesn't apply because assembly languages have only a small number of rudimentary data types such as "machine address" or "32 bit word". The concept that two data structures with an identical layout and low-level representation might be incompatible types does not occur in assembly language.

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

Even if two types look the same at the low level (another example: unsigned int and unsigned long are sometimes exactly the same) C compilers can optimize programs based on the assumption that the type rules have not been violated. For instance suppose that A and B point to the same memory location. If you assign to an an object A->member, a C compiler can assume that the object B->member is not affected by this, if A->member and B->member have incompatible types, like one being char * and the other void *. The generated code keeps caching the old value of B->member in a register, even though the in-memory copy was overwritten by the assignment to A->member. This is an example of invalid aliasing.

这篇关于将函数指针强制转换为空有什么作用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆