bar*_*ney 2 c c++ pointers function-pointers
找到这个代码示例
void *handle;
double (*cosine)(double);
handle = dlopen("libm.so", RTLD_LAZY);
*(void **) (&cosine) = dlsym(handle, "cos");
Run Code Online (Sandbox Code Playgroud)
我使用从右到左阅读规则来解析变量的类型:
double (*cosine)(double);
Run Code Online (Sandbox Code Playgroud)
在这里我从左到右书写但移动LTR:"余弦" - >"*" - >"是指针"然后"("我们走到最里面()范围 - >"(双)" - >"到函数取一个双" - >并返回最左边"双"
但这到底是什么意思?我甚至不知道从哪里开始解析)是"&cosine"的地址或参考?(void**)是什么意思?为什么它最左边的"*"?是取消引用还是类型?
*(void **) (&cosine)
Run Code Online (Sandbox Code Playgroud)
是的,那是满口的.
cosine
是一个函数指针.所以&cosine
是一个指向指针.然后当我们*
在它前面拍一个时,我们正在改变原始指针,使其指向其他地方.
它有点像这样:
int i = 5;
int *ip = &i;
*ip = 6; /* changes i to 6 */
Run Code Online (Sandbox Code Playgroud)
或者它更像是这样的:
char a[10], b[10];
char *p = a;
*(&p) = b; /* changes p to point to b */
Run Code Online (Sandbox Code Playgroud)
但在你的情况下,它甚至更棘手,因为它cosine
是一个指向函数的指针,而不是指向数据的指针.大多数情况下,函数指针指向您在程序中定义的函数.但是在这里,我们正在安排cosine
指向由dlsym()
函数加载的动态加载函数.
dlsym
是超级特殊的,因为它可以返回指向数据的指针,以及指向函数的指针.所以它有一个不可能定义的返回类型.void *
当然,它被声明为返回,因为它是C中的"泛型"指针类型.(想想malloc
.)但是在纯C中,void *
是通用数据指针类型; 它不能保证能够与函数指针一起使用.
直截了当的做法就是说
cosine = dlsym(handle, "cos");
Run Code Online (Sandbox Code Playgroud)
但是现代编译器会抱怨,因为dlsym
返回void *
,并且cosine
有类型double (*)(double)
(即,指向函数的指针需要加倍并返回double),这不是可移植的转换.
所以我们绕过谷仓,cosine
间接设定价值,而不是说
cosine = something
Run Code Online (Sandbox Code Playgroud)
而是说
*(&cosine) = something
Run Code Online (Sandbox Code Playgroud)
但在这种dlsym
情况下仍然没有好处,因为类型仍然不匹配.我们已经void *
在右边,所以我们需要void *
在左边.并且解决方案是获取地址&cosine
,否则它是指向函数的指针,并将其转换为指向指针的指针void
,或者void **
,当我们*
在它前面拍打我们时已经有了一个void *
再次,这是分配一个正确的目的地dlsym
的返回值.所以我们最终得到了你所问的那条线:
* (void **) (&cosine) = dlsym(handle, "cos");
Run Code Online (Sandbox Code Playgroud)
现在,重要的是要注意我们在这里处于薄弱环节.我们已经使用了&
和转换来解决这样一个事实,即指向void
指向函数的指针并不严格合法.在这个过程中,我们成功地压制了编译器的警告,即我们正在做的事情并非严格合法.(事实上,沉默警告正是原始程序员使用此躲闪的意图.)
潜在的问题是,如果数据指针和函数指针具有不同的大小或表示,该怎么办?这段代码用了一些长度来处理函数指针,cosine
好像它是一个数据指针,将数据指针的位干扰到它中.例如,如果数据指针比某个函数指针大,那么这将产生可怕的影响.(并且,在你问"但是数据指针怎么能比函数指针更大?"之后,就像在MS-DOS编程时代的"紧凑"内存模型中那样. )
通常,玩这样的游戏来破坏规则并关闭编译器警告是一个坏主意.dlsym
但是,在这种情况下,它很好,我会说完全可以接受. dlsym
在函数指针与数据指针不同的系统上不能存在,所以如果你正在使用它dlsym
,你必须在一台所有指针都相同的机器上,这个代码才有效.
这也值得一提,如果我们在调用时必须使用强制转换玩游戏dlsym
,为什么要用指针指针在barm周围进行额外的旅行呢?为什么不说
cosine = (double (*)(double))dlsym(handle, "cos");
Run Code Online (Sandbox Code Playgroud)
答案是,我不知道.我很确定这个更简单的演员也会同样有效(再次,只要我们在一个dlsym
可以存在的系统上).也许有编译器警告这种情况,只能通过使用tricker,双指针技巧来欺骗.
另请参阅使用dlsym()时的转换.
这是令人讨厌的东西.cosine
是一个指向函数的指针,该函数接受一个类型的参数double
并返回double
.&cosine
是该指针的地址.演员表示假装该地址是指针指向虚空的指针.在*
前面的演员中是通常引用操作,所以结果是告诉编译器假装的类型cosine
是void*
,这样的代码可以从通话中指定的返回值dlsym
来cosine
.唷; 那很痛.
并且,只是为了好玩,a void*
和指向函数的指针完全没有关系,这就是为什么代码必须经历所有的转换.C++语言定义不保证这将起作用.即,结果是未定义的行为.
归档时间: |
|
查看次数: |
171 次 |
最近记录: |