Linux 是如何隐藏`DIR`结构体定义的

95 天前
 vituralfuture

最近在看 APUE 时遇到了DIR,提到了由于不同的实现,解析目录文件内容方式不统一,通常会阻止直接读取目录文件的内容

DIR *opendir (const char *name)

跳转到DIR的定义

/* This is the data type of directory stream objects.
   The actual structure is opaque to users.  */
typedef struct __dirstream DIR;

__dirstream定义找不到
注释说 DIR 结构体对用户透明,那么它是如何隐藏这个定义的呢?

源文件

#include <stdio.h>
#include <dirent.h>

int main() {
    DIR* dir = opendir(".");
    struct dirent *d = readdir(dir);
    while (d) {
        printf("%s\n", d->d_name);
        d = readdir(dir);
    }
    return 0;
}

使用 gcc 预处理源文件

➜  test gcc -E ls.c | grep __dirstream -A2 -B2
  };
# 127 "/usr/include/dirent.h" 3 4
typedef struct __dirstream DIR;


并没有找到__dirstream的定义

所以是如何实现隐藏定义的?

1778 次点击
所在节点    Linux
6 条回复
geelaw
95 天前
C 语言不要求所有 struct 都有定义,只要声明之后就可以使用指针。(当然用 sizeof 作用,或者定义该类型对象或数组,是需要该结构体的定义的。)所谓它是 opaque 就是说不提供定义。

从 C 的 ABI 的角度,结构体指针和 void 指针没啥区别。实现 opendir 的人可能知道 DIR 的定义,并分配好内存、填充好数据返回给调用者。
geelaw
95 天前
举个例子:

// a.c
#include<malloc>
typedef struct a { int b; } a;
a *foo(void)
{
return (a *)malloc(sizeof(a));
}

编译 a.c 之后得到 a.obj ,删去 a.c

// b.c
typedef struct a a;
a *foo(void);
int main(void) { foo(); }

编译 b.c 并和 a.obj 链接。结果是 b 可以正常执行,在 b 产生的时候不需要 a.c 的存在。

现在的状况就是 opendir 在别人写的 a.c 里面,但别人没有提供 a.c 而是提供了 a.obj ,而别人提供的 .h 是上面 b.c 的前两行。
yanqiyu
95 天前
因为除了 libc 内部之外不需要接触到 DIR 这个结构的成员

结构体的定义就是告诉编译器,结构体的成员排布(每个成员的偏移,结构体的大小),要是编译器用不到这些信息就不会要求必须看到定义。( C++ 管这叫做 odr-use ,但是不知道 C 有没有类似的术语,也可以类比前向声明的时候不需要具体定义)

要是没有用到具体定义的翻译单元,就没必要让编译器看到结构体的定义。然后包含这个结构体的定义的头文件大概没有被发布出来,只是被一些内部函数的定义的代码用到了(就是编译 libc 的时候有,但是在 libc 安装的时候没有被拷贝出来)。
vituralfuture
95 天前
理解了,这个`DIR`就是一个不完整的类型,因为只有声明,能通过编译,只能当作指针使用,而它的成员、大小都是未知的

如果对`DIR`使用`sizeof`或者指针运算,就无法通过编译
lance6716
95 天前
原来更高级语言的抽象啊接口啊,在 C 里是这样的。学习了
fpk5
95 天前
library 一般通过提供结构的 declaration 而没有 definition 的方式来隐藏内部实现的(类比 private 成员)。对于内部成员的操作需要全部通过函数暴露出来,函数没有提供的操作就是 private 的方便库作者后面更改。`__dirstream` 在 linux 里的 definition 在 glibc 里 https://elixir.bootlin.com/glibc/latest/source/sysdeps/unix/sysv/linux/dirstream.h#L30

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1015775

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX