Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/145.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么C/C+;中的函数指针和数据指针不兼容+;?_C++_C_Pointers_Function Pointers - Fatal编程技术网

C++ 为什么C/C+;中的函数指针和数据指针不兼容+;?

C++ 为什么C/C+;中的函数指针和数据指针不兼容+;?,c++,c,pointers,function-pointers,C++,C,Pointers,Function Pointers,我已经读到,将函数指针转换为数据指针,或者将函数指针转换为数据指针,在大多数平台上都可以工作,但不能保证工作正常。为什么会这样?这两者不应该都只是主内存中的地址,因此是兼容的吗?根据目标体系结构,代码和数据可能存储在根本不兼容的、物理上不同的内存区域中 指向void的指针应该能够容纳指向任何类型数据的指针,但不一定是指向函数的指针。有些系统对指向函数的指针的要求与指向数据的指针的要求不同(例如,有些DSP对数据和代码的寻址不同,MS-DOS上的介质型号对代码使用32位指针,但对数据只使用16位指

我已经读到,将函数指针转换为数据指针,或者将函数指针转换为数据指针,在大多数平台上都可以工作,但不能保证工作正常。为什么会这样?这两者不应该都只是主内存中的地址,因此是兼容的吗?

根据目标体系结构,代码和数据可能存储在根本不兼容的、物理上不同的内存区域中

指向void的指针应该能够容纳指向任何类型数据的指针,但不一定是指向函数的指针。有些系统对指向函数的指针的要求与指向数据的指针的要求不同(例如,有些DSP对数据和代码的寻址不同,MS-DOS上的介质型号对代码使用32位指针,但对数据只使用16位指针)。

未定义并不一定意味着不允许,这可能意味着编译器实现者有更多的自由去做他们想做的事情


例如,在某些体系结构上可能不可能做到这一点-未定义允许它们仍然具有一致的“C”库,即使您无法做到这一点。

唯一真正可移植的解决方案是不使用函数的
dlsym
,而是使用
dlsym
来获取指向包含函数指针的数据的指针。例如,在您的库中:

struct module foo_module = {
    .create = create_func,
    .destroy = destroy_func,
    .write = write_func,
    /* ... */
};
然后在您的应用程序中:

struct module *foo = dlsym(handle, "foo_module");
foo->create(/*...*/);
/* ... */
顺便说一句,这是一种很好的设计实践,可以很容易地通过
dlopen
支持动态加载和静态链接不支持动态链接的系统上的所有模块,或者用户/系统集成商不希望使用动态链接的系统上的所有模块。

另一种解决方案:

假设POSIX保证函数指针和数据指针具有相同的大小和表示形式(我找不到这方面的文本,但引用的示例OP表明它们至少有意提出这一要求),那么以下应该可以工作:

double (*cosine)(double);
void *tmp;
handle = dlopen("libm.so", RTLD_LAZY);
tmp = dlsym(handle, "cos");
memcpy(&cosine, &tmp, sizeof cosine);
这可以通过使用
char[]
表示避免违反别名规则,该表示允许对所有类型进行别名

另一种方法:

union {
    double (*fptr)(double);
    void *dptr;
} u;
u.dptr = dlsym(handle, "cos");
cosine = u.fptr;

但是,如果您想要绝对100%正确的C,我建议使用
memcpy
方法。

它们可以是不同类型,具有不同的空间要求。赋值给一个指针可以不可逆地分割指针的值,这样赋值回来会产生不同的结果

我相信它们可以是不同的类型,因为标准不想限制在不需要时节省空间的可能实现,或者当它的大小可能导致CPU不得不额外使用它时,等等。

一些计算机有单独的代码和数据地址空间。在这样的硬件上,它根本不起作用

该语言不仅设计用于当前的桌面应用程序,而且允许在大型硬件上实现


似乎C语言委员会从未打算将
void*
作为指向函数的指针,他们只是想要一个指向对象的通用指针

C99的理由是:

6.3.2.3指针 C现在已经在广泛的体系结构上实现。而其中一些 体系结构的特点是统一指针的大小最大为某种整数类型 可移植代码不能假定不同指针类型和整数类型之间存在任何必要的对应关系。在某些实现中,指针甚至可以比任何整数类型都宽

使用
void*
(“指向
void
的指针”)作为通用对象指针类型是C89委员会的发明。采用这种类型是因为希望指定函数原型参数,这些参数要么悄悄地转换任意指针(如
fread
),要么在参数类型不完全匹配时抱怨(如
strcmp
)。对于指向函数的指针没有任何说明,这些指针可能与对象指针和/或整数不一致


注意,在最后一段中没有提到函数指针。它们可能不同于其他指针,委员会意识到这一点。

体系结构不必将代码和数据存储在同一内存中。在哈佛体系结构中,代码和数据存储在完全不同的内存中。大多数体系结构都是冯·诺依曼体系结构,代码和数据都在同一内存中,但如果可能的话,C并不局限于某些类型的体系结构。

除了这里已经提到的内容之外,还可以看看POSIX:

ISO C标准不要求函数指针可以来回转换为数据指针。实际上,ISO C标准并不要求void*类型的对象可以持有指向函数的指针。但是,支持XSI扩展的实现确实要求void*类型的对象可以包含指向函数的指针。但是,将指向函数的指针转换为指向其他数据类型(void*除外)的指针的结果仍然没有定义。请注意,如果尝试从void*指针转换为函数指针,则符合ISO C标准的编译器需要生成警告,如下所示:

 fptr = (int (*)(int))dlsym(handle, "my_function");
由于这里提到的问题,未来的版本可能会添加一个新函数来返回函数指针,或者当前接口可能会被弃用,转而使用两个新函数:一个返回数据指针,另一个返回函数指针


对于那些记得MS-DOS、Windows3.1及更老版本的人来说,答案很简单。所有这些都用于支持几种不同的内存模型,代码和数据指针的特征组合各不相同

例如,对于紧凑型模型(小代码、大数据):

相反,在中等模型中(大代码、小数据):

sizeof(void*)
在这种情况下,您没有单独存储代码和日期,但仍然不能
sizeof(void *) > sizeof(void(*)())
sizeof(void *) < sizeof(void(*)())
class Base1 { int b1; void Base1Method(); };
class Base2 { int b2; void Base2Method(); };
class Derived : public Base1, Base2 { int d; void DerivedMethod(); };
[Address of function]
[Adjustor]
struct { 
    void * func;
    size_t offset;
}