十分好奇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*,参数为传入的任意数据的类型,最后强制转换fPtr为funcPtr类型的数据并调用它并返回值。
调用它,成功。
这里可能会有疑惑:明明runFunc中调用强制转换的数据为void*,为何最后返回输出还是int的456?不应该是指向int数据456的指针吗?
没有错,因为void*存储了原本返回456的int数据,相当于强制将int 456转成了void* 456。
此时我们多给参数不会产生结果上的偏差,但是少给参数:
可能因为指针越界了导致add123执行的加法运算的数据变成了野指针的数据抑或是野指针本身。
上上面由于void*的存储空间很小,一旦返回的数据是一个超出该类型存储范围的数据将获取不到数据,例如一些对象。。。