为什么函数指针和数据指针在C / C ++不兼容?(Why are function pointer

2019-06-18 02:35发布

我已阅读,一个函数指针转换为数据指针,反之亦然在大多数平台上工作,但不能保证正常工作。 为什么会出现这样的情况? 不应同时简单地解决到主存储器中,因此兼容?

Answer 1:

体系架构没有存储代码和数据在相同的存储。 具有哈佛结构,代码和数据都存储在完全不同的存储器中。 大多数架构冯·诺依曼架构在同一个存储代码和数据,但C没有自身仅限制于某些类型的,如果在所有可能的架构。



Answer 2:

有几台计算机(有)代码和数据独立的地址空间。 在这样的硬件,它是行不通的。

语言不仅是当前的桌面应用而设计的,而是允许它在一个大组硬件的实现。


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

C99的理由说:

6.3.2.3指针
C已现在已经在广泛的架构来实现。 虽然这些体系结构的功能均匀指针其是某个整数类型的大小,最大限度移植代码不能假定不同指针类型和整数类型之间的任何必要的对应关系。 上一些实施方式中,指针可以甚至比任何整数类型更宽。

使用void* (“指针void作为一个通用对象指针型”)是C89委员会的发明。 这种类型的通过是通过指定函数原型的参数,要么悄悄地转换任意指针(如欲望刺激fread ),或抱怨,如果参数类型不完全匹配(如strcmp )。 没有提到函数指针,这可能是不相称与对象指针和/或整数。

注意没有谈及函数指针在最后一段。 他们可能是从其他指针不同,该委员会是意识到这一点。



Answer 3:

对于那些谁记得MS-DOS,Windows 3.1和较旧的答案是很容易的。 所有这些用于支持若干不同的存储模型,以用于代码和数据的指针变化的特性的组合。

因此,例如对于紧凑型(小码,大数据):

sizeof(void *) > sizeof(void(*)())

并且相反地在介质模型(大的代码,小的数据):

sizeof(void *) < sizeof(void(*)())

在这种情况下,你没有对代码和日期分开存储,但还是没能在两个指针之间进行转换(短期使用非标准__near和__far修饰符)。

此外,还有没有保证,即使指针是相同的大小,它们指向同样的事情 - 在DOS小存储模型,代码和数据使用近指针,但他们指出,不同的细分。 因此,一个函数指针转换为数据指针不会给你一个指针必须在所有的功能没有任何关系,因此没有使用了这种转换。



Answer 4:

指向void应该是能够容纳一个指向任何类型的数据 - 但不一定是指向一个功能。 有些系统具有用于函数指针比指针数据不同要求(例如,存在与不同的DSP寻址数据与代码,在MS-DOS介质模型用于代码32位指针但对于数据唯一的16位指针) 。



Answer 5:

除了什么是已经在这里说,有趣的是,看POSIX dlsym()

ISO C标准不要求函数指针可以投来回指针数据。 事实上,ISO C标准并不要求void *类型的对象可以容纳一个指针的函数。 实现支持XSI扩展,但是,也需要void *类型的对象可以容纳一个指针的函数。 一个指针转换为函数成指向另一个数据类型(除了无效*)的结果仍然是不确定的,但是。 需要注意的是编译器符合ISO C标准,需要生成一个警告,如果从一个void *指向一个函数指针转换尝试,如:

  fptr = (int (*)(int))dlsym(handle, "my_function"); 

由于这里的问题指出,未来的版本中可能需要添加新的函数返回函数指针,或者当前的接口可以支持两个新的功能被弃用:一个是返回数据指针和返回函数指针其他。



Answer 6:

C ++ 11具有溶液到C / C ++和POSIX之间的长期失配相对于dlsym() 我们可以使用reinterpret_cast函数指针从数据指针转换为/,只要实现支持此功能。

从标准,5.2.10段。 如图8所示,“一个函数指针转换为对象的指针型,反之亦然是有条件地支持”。 1.3.5定义“有条件地支持”作为“一个实现不需要支持程序构建体”。



Answer 7:

根据目标架构,代码和数据可以被存储在存储器根本不相容的,物理上不同的区域。



Answer 8:

未定义的并不一定意味着不允许的,这可能意味着,编译器实现者更多的自由去做他们希望如何。

例如,它可能无法在某些体系结构 - 未定义允许他们仍然有一个符合“C”库,即使你不能做到这一点。



Answer 9:

它们可以是不同类型具有不同的空间需求。 分配到一个无法挽回的切片指针的值,以便分配回的东西不同的结果。

我相信他们可以是不同类型的,因为标准不希望限制节省空间是没有必要的,或者当规模可能导致CPU必须做一些额外的废话使用它,等它的时候可能实现?



Answer 10:

另一种解决方案:

假设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;

不过,我会建议memcpy方法,如果你想绝对100%正确C.



Answer 11:

唯一的真正的便携式解决方案是不使用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和静态链接上不支持动态链接,或者在用户/系统集成商不希望使用动态链接系统中的所有模块。



Answer 12:

在大多数结构中,指针的所有正常数据类型具有相同的显示,所以数据指针类型之间铸造是一个空操作。

然而,这是可以想象的函数指针可能需要不同的表示,也许他们比其他指针大。 如果无效*可容纳函数指针,这将意味着无效*的代表必须是尺寸较大。 和数据指针的所有类型转换到/从无效*必须执行这些额外的副本。

正如有人所说,如果你需要这个,你使用的国家可以实现它。 但是无效*的大多数使用只是数据,所以这将是繁重的,以增加他们所有的内存使用,以防万一一个函数指针需要存储。



Answer 13:

C ++的类的成员函数指针 :其中的函数指针可以在从数据指针大小相异程度的现代的例子

从直接引用https://blogs.msdn.microsoft.com/oldnewthing/20040209-00/?p=40713/

 class Base1 { int b1; void Base1Method(); }; class Base2 { int b2; void Base2Method(); }; class Derived : public Base1, Base2 { int d; void DerivedMethod(); }; 

现在有两种可能this指针。

一个指针,指向的成员函数Base1可被用作一个指向的一个成员函数Derived的,因为它们都使用相同的this指针。 但一个指针的成员函数Base2不能原样使用作为指针的成员函数Derived ,由于this指针需要调整。

有解决的许多方面。 下面是在Visual Studio编译器决定如何处理它:

指向一个乘法 - 继承的类的成员函数是一个真正的结构。

 [Address of function] [Adjustor] 

使用多重继承的类的一个指针到成员函数的大小是一个指针的大小加上一个的大小size_t

TL; DR:当使用多重继承,一个指针指向一个成员函数可以(取决于编译器,版本,建筑等)实际上被存储作为

struct { 
    void * func;
    size_t offset;
}

这显然不是一个更大的void *



Answer 14:

我知道,这还没有被2012年以来评论,但我想补充一点,我知道 ,有数据和功能不相称的指针,因为对建筑的检查权限的呼叫和承载额外信息的架构将是有益的。 铸造没有量会有所帮助。 这是穆勒 。



文章来源: Why are function pointers and data pointers incompatible in C/C++?