C++ 如果余弦是fptr,如何解析*(void**)(&;cosine)
找到这个代码示例C++ 如果余弦是fptr,如何解析*(void**)(&;cosine),c++,c,pointers,function-pointers,C++,C,Pointers,Function Pointers,找到这个代码示例 void *handle; double (*cosine)(double); handle = dlopen("libm.so", RTLD_LAZY); *(void **) (&cosine) = dlsym(handle, "cos"); 我使用从右向左的读取规则来解析变量的类型: double (*cosine)(double); 这里我从左向右写,但移动LTR:“余弦”->“*”->“是指针” 然后“(“我们在最里面的()范围之外->”(double)“
void *handle;
double (*cosine)(double);
handle = dlopen("libm.so", RTLD_LAZY);
*(void **) (&cosine) = dlsym(handle, "cos");
我使用从右向左的读取规则来解析变量的类型:
double (*cosine)(double);
这里我从左向右写,但移动LTR:“余弦”->“*”->“是指针”
然后“(“我们在最里面的()范围之外->”(double)“->”函数取一个double->并返回最左边的“double”
但这到底是什么?我甚至不知道从哪里开始解析“&余弦”是地址还是引用?(空白**)是什么意思?为什么外面有最左边的“*”???是取消引用还是键入
*(void **) (&cosine)
是的,那是一口
cosine
是一个函数指针。因此,&余弦
是指向该指针的指针。然后当我们在它前面拍一个*
时,我们改变了原始指针,使它指向其他地方
有点像这样:
int i = 5;
int *ip = &i;
*ip = 6; /* changes i to 6 */
char a[10], b[10];
char *p = a;
*(&p) = b; /* changes p to point to b */
或者更像这样:
int i = 5;
int *ip = &i;
*ip = 6; /* changes i to 6 */
char a[10], b[10];
char *p = a;
*(&p) = b; /* changes p to point to b */
但在您的例子中,它甚至更为棘手,因为余弦
是指向函数的指针,而不是指向数据的指针。大多数情况下,函数指针指向您在程序中定义的函数。但是在这里,我们安排让cosine
指向一个动态加载的函数,由dlsym()
函数加载
dlsym
非常特殊,因为它可以返回指向数据的指针以及指向函数的指针。因此,不可能定义返回类型。当然,它被声明为返回void*
,因为这是C中的“通用”指针类型(想想malloc
),但在纯C中,void*
是通用的数据指针类型;它不能保证能够与函数指针一起使用
最简单的方法就是说
cosine = dlsym(handle, "cos");
但现代编译器会抱怨,因为dlsym
返回void*
,而cosine
具有类型double(*)(double)
(即指向函数取双精度并返回双精度的指针),这不是可移植的转换
所以我们绕着谷仓转,间接地设置余弦的值,而不是说
cosine = something
*(&cosine) = something
而是说
cosine = something
*(&cosine) = something
但是在dlsym
的情况下,这仍然是不好的,因为类型仍然不匹配。右边有void*
,所以左边需要void*
。解决方法是取地址&余弦,它是指向函数的指针,然后将它转换为指向-void
,或void**
,这样当我们在它前面打一个*
时,我们又得到了一个void*
,哪个是分配dlsym
返回值的正确目标。因此,我们以您询问的那句话结束:
* (void **) (&cosine) = dlsym(handle, "cos");
现在,重要的是要注意,我们在这里是在薄冰上。我们使用了&
和cast来回避这样一个事实:将指向-void
的指针指定给`指向函数的指针'并不是严格合法的。在这个过程中,我们成功地消除了编译器关于我们所做的不完全合法的警告。(事实上,让警告静音正是程序员使用这种闪避的初衷。)
潜在的问题是,如果数据指针和函数指针具有不同的大小或表示,该怎么办?这段代码经过一定长度,将函数指针余弦当作数据指针,将数据指针的位塞入其中。比如说,如果数据指针比函数指针大,这将产生可怕的影响。(在你问“但是数据指针怎么可能比函数指针大呢?”之前,这正是在MS-DOS编程时代,例如在“紧凑”内存模型中的情况。)
通常,玩这样的游戏来打破规则并关闭编译器警告是个坏主意。但是,对于dlsym
,我认为这是完全可以接受的dlsym
不能存在于函数指针与数据指针不同的系统上,因此如果您使用的是dlsym
,则您必须位于所有指针都相同的机器上,并且此代码可以工作
同样值得一问的是,如果我们必须在调用dlsym
时使用演员扮演游戏,为什么还要用指针指向指针在酒吧周围进行额外的旅行呢?为什么不直接说呢
cosine = (double (*)(double))dlsym(handle, "cos");
答案是,我不知道。我敢肯定,这个简单的cast也同样有效(同样,只要我们在一个dlsym
可以存在的系统上)。也许有编译器警告这种情况,只有使用欺骗者的双指针技巧才能使其保持沉默
另请参见。这是令人讨厌的东西cosine
是指向函数的指针,该函数接受类型为double
的参数并返回double
&余弦
是该指针的地址。演员说假装那个地址是指向void的指针。cast前面的*
是常用的解引用操作符,因此结果是告诉编译器假装余弦的类型是void*
,这样代码就可以将调用的返回值分配给dlsym
到余弦。呸;那很痛
而且,只是为了好玩,一个void*
和一个指向函数的指针根本不相关,这就是为什么代码必须经过所有的转换。C++语言定义不能保证这能起作用。也就是说,结果是未定义的行为。对于C,指向void
的指针可以转换为指向对象的任何指针,而无需强制转换。但是,C标准不能保证void*
可以转换为指向函数的指针,因为函数不是对象
dlsym
是一个POSIX函数;POSIX要求,作为扩展,指向函数的指针必须可转换为<代码