C++ void*与函数指针的探讨

十分好奇Python的ctypes是如何直接调用未知类型的函数指针的。

于是生成了一个dll:

(test函数忽略不看)

发现char类型和bool类型返回的数据都类似一个指针,而尝试给该函数更多的参数或更少的参数将会发生:

说明ctypes调用dll函数大致是忽略了函数的返回类型(可能?抑或是推断):

ctypes的源码中发现address事实上是一个void*(即任意指针)的玩意。

About “void*”

void*可谓一个神奇的东西,在C++这个处处强类型,不知道类型几乎寸步难行的地方却能容纳下任意的类型。实际上这个东西也没啥神奇的,void本来表示的就是未知类型,只是经常当作空来使用。

void*不过是一个指向任意数据类型的指针:

如上面的void*指向了内存的0x01,但是不知道该内存存储的数据类型,所以你要使用void*指向的内存存储的数据时需强制转换为它实际的类型。

但是这种转换实际上是unsafe的,如果void*这个指针指向了一个空内存、其他程序的内存等等,所以Know first do later。

void* meets functions

当void*碰上函数,一切都好玩起来了。

就像上文所说,void*可以存很多东西,函数当然也可以。

我们平时所说的函数指针一般都这样:

我们可以这样定义他的指针:

亦或者:

然后可以这样定义一个指向add的函数指针:

这时ptr就等同于add123的别名了。

如上图,当我们强制把add123转换为一个void*指针(指针本质上是一串数字),输出就会变成指针的二进制转成整数(因为%d代表整数)

此时再将ptr2(void* ptr2 = (void*) add123;)强制转换为addPtr类型的数据,再调用,成功输出579。

套娃

我们写了一个模板函数,该函数可以传入一个指针fPtr,并接上任意数据(例如int, int, char…),随后定义了funcPtr为一个返回值为void*,参数为传入的任意数据的类型,最后强制转换fPtrfuncPtr类型的数据并调用它并返回值。

调用它,成功。

这里可能会有疑惑:明明runFunc中调用强制转换的数据为void*,为何最后返回输出还是int的456?不应该是指向int数据456的指针吗?

没有错,因为void*存储了原本返回456的int数据,相当于强制将int 456转成了void* 456。

此时我们多给参数不会产生结果上的偏差,但是少给参数:

可能因为指针越界了导致add123执行的加法运算的数据变成了野指针的数据抑或是野指针本身。

上上面由于void*的存储空间很小,一旦返回的数据是一个超出该类型存储范围的数据将获取不到数据,例如一些对象。。。

发表回复