Linux 是如何隐藏`DIR`结构体定义的 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
vituralfuture
V2EX    Linux

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

  •  
  •   vituralfuture 2024 年 2 月 15 日 2823 次点击
    这是一个创建于 799 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近在看 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的定义

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

    6 条回复    2024-02-16 02:34:16 +08:00
    geelaw
        1
    geelaw  
       2024 年 2 月 15 日 via iPhone   2
    C 语言不要求所有 struct 都有定义,只要声明之后就可以使用指针。(当然用 sizeof 作用,或者定义该类型对象或数组,是需要该结构体的定义的。)所谓它是 opaque 就是说不提供定义。

    从 C 的 ABI 的角度,结构体指针和 void 指针没啥区别。实现 opendir 的人可能知道 DIR 的定义,并分配好内存、填充好数据返回给调用者。
    geelaw
        2
    geelaw  
       2024 年 2 月 15 日 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 的前两行。
    yanqiyu
        3
    yanqiyu  
       2024 年 2 月 15 日
    因为除了 libc 内部之外不需要接触到 DIR 这个结构的成员

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

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

    如果对`DIR`使用`sizeof`或者指针运算,就无法通过编译
    lance6716
        5
    lance6716  
       2024 年 2 月 16 日 via Android
    原来更高级语言的抽象啊接口啊,在 C 里是这样的。学习了
    fpk5
        6
    fpk5  
       2024 年 2 月 16 日
    library 一般通过提供结构的 declaration 而没有 definition 的方式来隐藏内部实现的(类比 private 成员)。对于内部成员的操作需要全部通过函数暴露出来,函数没有提供的操作就是 private 的方便库作者后面更改。`__dirstream` 在 linux 里的 definition 在 glibc 里 https://elixir.bootlin.com/glibc/latest/source/sysdeps/unix/sysv/linux/dirstream.h#L30
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     910 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 32ms UTC 19:39 PVG 03:39 LAX 12:39 JFK 15:39
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86