Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/65.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
(struct*)vs(void*)——C11/C99中的功能原型等效_C_Posix_Function Pointers_C11_Function Prototypes - Fatal编程技术网

(struct*)vs(void*)——C11/C99中的功能原型等效

(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 *); ..

昨晚我试图实现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 *);
...
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节(功能声明器(包括原型))似乎证实了这一点

因此,从理论上讲,应采取以下措施:

现在我在BSD和GNULIBC代码中看到了类似的事情,这很有趣

这些转换的等价性是来自编译器的实现工件的结果,还是可以从标准规范推断出的基本限制/属性

这会导致未定义的行为吗

@恩韦尔霍夫说:

对于要兼容的两种指针类型,两者应相同 合格,且两者都应是指向兼容类型的指针

好的,这是钥匙。
(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*)
  • 等等,具有所有特殊属性
来自C99标准,6.7.5.1指针声明器:

对于要兼容的两种指针类型,两者都应具有相同的资格,并且都应是指向兼容类型的指针

所以
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;