(struct*)vs(void*)——C11/C99中的功能原型等效
昨晚我试图实现GLOB_ALTDIRFUNC,突然遇到了一个有趣的问题 虽然语义上可能稍有不同,但是(struct*)vs(void*)——C11/C99中的功能原型等效,c,posix,function-pointers,c11,function-prototypes,C,Posix,Function Pointers,C11,Function Prototypes,昨晚我试图实现GLOB_ALTDIRFUNC,突然遇到了一个有趣的问题 虽然语义上可能稍有不同,但是(void*)和(struct*)类型是否等效 示例代码: typedef struct __dirstream DIR; struct dirent *readdir(DIR *); DIR *opendir(const char *); ... struct dirent *(*gl_readdir)(void *); void *(*gl_opendir)(const char *); ..
(void*)
和(struct*)
类型是否等效
示例代码:
typedef struct __dirstream DIR;
struct dirent *readdir(DIR *);
DIR *opendir(const char *);
...
struct dirent *(*gl_readdir)(void *);
void *(*gl_opendir)(const char *);
...
gl_readdir = (struct dirent *(*)(void *))readdir;
gl_opendir = (void *(*)(const char *))opendir;
...
DIR *x = gl_opendir(".");
struct dirent *y = gl_readdir(x);
...
我的直觉是这样说的;它们具有基本相同的存储/表示/对齐要求;对于参数和返回类型,它们应该是等价的
和的第6.2.5节(类型)和第6.7.6.3节(功能声明器(包括原型))似乎证实了这一点
因此,从理论上讲,应采取以下措施:
(void*)
和(struct*)
怎么会不兼容
从6.3.2.3指针:
指向一种类型函数的指针可以转换为指向另一种类型函数的指针,然后再转换回来;结果应进行比较
等于原始指针。如果使用转换的指针调用
类型与指向类型不兼容的函数
行为是未定义的
还没有决定
进一步澄清:
- 结构依赖于它们的第一个元素进行对齐,所以结构指针的对齐要求应该与空指针的对齐要求相同,对吗
- 我最初没有在任何地方指定
,但它保证是一个结构DIR
- 问题的关键是知道我是否可以避免包装(就像我为gl_closedir所做的那样,它的类型显然是不兼容的)
- 虽然C11/C99可能不允许这样做,但实际上BSD和GNU系统代码使用了这样的方法,因此可能有其他相关标准(如POSIX)指定了这种行为
- (ABI不应更改,由
->\u GNU\u SOURCE
控制)\u USE\u GNU
- 我想不出任何理由你不能用
交换(void*)
,反之亦然(struct*)
将与第一个元素对齐,这可能是struct
,因此指针的要求与char
指针的要求完全相同void
- 因此,
指针应该与struct
指针具有相同的实现要求,所有void
指针都必须等效,这进一步加强了这一点struct
- 所以
应该等同于(const void*)
(const struct*)
- 和
应等同于(void*)
(struct*)
- 等等,具有所有特殊属性
void*
和DIR*
不兼容
从6.7.5.3功能声明器(包括原型):
对于要兼容的两种功能类型,两者都应指定兼容的返回类型。此外,参数类型列表(如果两者都存在)应在参数数量和省略号终止符的使用方面保持一致;相应参数应具有兼容类型
因此struct dirent*(*)(void*)
(gl_readdir的类型)和struct dirent*(*)(DIR*)
(readdir的类型)不兼容
从6.3.2.3指针:
指向一种类型函数的指针可以转换为指向另一种类型函数的指针,然后再转换回来;结果应与原始指针进行比较。如果使用转换的指针调用类型与指向类型不兼容的函数,则行为未定义
所以
是未定义的行为。
struct x*
和struct y*
对于任意两个x
和y
保证具有相同的表示和对齐要求,对于联合指针相同,但不是无效指针和结构指针:
指向void的指针应具有相同的表示和对齐方式
要求作为指向字符类型的指针。48)类似地,指针
兼容类型的合格或不合格版本应具有
相同的表示和对齐要求。所有指向
结构类型应具有相同的表示和对齐方式
需求是相互关联的。所有指向联合类型的指针应具有
表示和对齐要求彼此相同。指针
到其他类型不需要具有相同的表示或对齐方式
要求
此外,函数类型的“子类型”的相同表示和对齐要求是不够的。对于通过要定义的函数指针进行的调用,函数指针的目标类型必须与实际函数的类型兼容,对于函数兼容性,需要在相应的函数参数之间严格兼容,这在技术上意味着,例如,void foo(char*)代码>与void foo(char const*)不兼容代码>即使char*
和char const*
具有相同的表示和对齐方式
要使两种函数类型兼容,则
gl_readdir = (struct dirent *(*)(void *))readdir;
gl_readdir(x);
struct dirent;
typedef /* opaque */ DIR;
extern struct dirent *readdir (DIR *);
struct dirent *(*gl_readdir)(void *);
gl_readdir = (struct dirent *(*)(void *))readdir;
DIR *x = /* ... */;
struct dirent *y = gl_readdir(x);
static struct dirent *
gl_readdir_glue (void *closure)
{
return readdir((DIR *)closure);
}
gl_readdir = gl_readdir_glue;