最近在看 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
的定义
所以是如何实现隐藏定义的?
1
geelaw 307 天前 via iPhone 2
C 语言不要求所有 struct 都有定义,只要声明之后就可以使用指针。(当然用 sizeof 作用,或者定义该类型对象或数组,是需要该结构体的定义的。)所谓它是 opaque 就是说不提供定义。
从 C 的 ABI 的角度,结构体指针和 void 指针没啥区别。实现 opendir 的人可能知道 DIR 的定义,并分配好内存、填充好数据返回给调用者。 |
2
geelaw 307 天前 via iPhone
举个例子:
// 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 的前两行。 |
3
yanqiyu 307 天前
因为除了 libc 内部之外不需要接触到 DIR 这个结构的成员
结构体的定义就是告诉编译器,结构体的成员排布(每个成员的偏移,结构体的大小),要是编译器用不到这些信息就不会要求必须看到定义。( C++ 管这叫做 odr-use ,但是不知道 C 有没有类似的术语,也可以类比前向声明的时候不需要具体定义) 要是没有用到具体定义的翻译单元,就没必要让编译器看到结构体的定义。然后包含这个结构体的定义的头文件大概没有被发布出来,只是被一些内部函数的定义的代码用到了(就是编译 libc 的时候有,但是在 libc 安装的时候没有被拷贝出来)。 |
4
vituralfuture OP 理解了,这个`DIR`就是一个不完整的类型,因为只有声明,能通过编译,只能当作指针使用,而它的成员、大小都是未知的
如果对`DIR`使用`sizeof`或者指针运算,就无法通过编译 |
5
lance6716 307 天前 via Android
原来更高级语言的抽象啊接口啊,在 C 里是这样的。学习了
|
6
fpk5 307 天前
library 一般通过提供结构的 declaration 而没有 definition 的方式来隐藏内部实现的(类比 private 成员)。对于内部成员的操作需要全部通过函数暴露出来,函数没有提供的操作就是 private 的方便库作者后面更改。`__dirstream` 在 linux 里的 definition 在 glibc 里 https://elixir.bootlin.com/glibc/latest/source/sysdeps/unix/sysv/linux/dirstream.h#L30 。
|