V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
thomaswang
V2EX  ›  问与答

c 工程在 main.c 里面引入其他模块,为什么要引入这个模块的.h 文件呢

  •  
  •   thomaswang · 2018-07-27 09:41:55 +08:00 · 3113 次点击
    这是一个创建于 2318 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最好能讲解的详细点, 多谢

    编译,汇编,链接,可重定位目标文件,符号,符号表

    23 条回复    2018-07-27 21:00:36 +08:00
    ian19znj
        1
    ian19znj  
       2018-07-27 09:53:26 +08:00
    因为需要知道函数,变量以及宏的声明,否则无法调用。
    msg7086
        2
    msg7086  
       2018-07-27 10:22:48 +08:00
    不一定要引入这个模块的.h 文件。

    只要在调用前申明你所用到的文件外的函数、变量还有宏之类的就行了。
    .h 文件只是简化这个过程。
    LuckyKoala
        3
    LuckyKoala  
       2018-07-27 10:40:12 +08:00 via Android
    分离接口和实现,把一个模块的函数,变量声明放到头文件中,具体实现也在同名.c 文件中。

    算是规范吧,你不用头文件也是可以的。

    稍大一点的项目,不同的开发者开发时只负责自己的一部分,但是需要知道其它部分的接口,这个时候就可以查看头文件,但不查看具体实现,避免过分依赖其他模块的实现。
    LuckyKoala
        4
    LuckyKoala  
       2018-07-27 10:46:58 +08:00 via Android
    https://gcc.gnu.org/onlinedocs/gcc-3.0.2/cpp_2.html

    头文件有两个来源,一个是系统提供的接口,还有一个就是用户自定义的。

    使用头文件还有一个好处就是所有一组的声明放在一个头文件中,外部通过 include 头文件来包含内容,需要修改声明的时候,修改一个头文件就行了,对应的引用这个头文件的地方也就应用修改了。
    GPIO
        5
    GPIO  
       2018-07-27 10:47:48 +08:00
    码农翻身之前有篇文章叫《真正的程序员都应该搞清楚编译和链接》,本来想发链接给你的,不过要手机号验证所以作罢,你自己搜一下。
    GeruzoniAnsasu
        6
    GeruzoniAnsasu  
       2018-07-27 10:50:09 +08:00
    其实可以完全不需要头文件

    你试试把需要的函数一个一个手动声明,也是照样可以正常链接的

    但这不是,慢嘛,有能复用的声明干嘛要手动重写一遍?
    thomaswang
        7
    thomaswang  
    OP
       2018-07-27 10:55:26 +08:00
    @LuckyKoala 当 main 这个函数编译的时候,会有个 elf,里面有符号表,里面有一个符号指到其他模块的函数,这里面如何用到别的模块的函数定义,肯定是用了,但是不知道如何用的
    Nitroethane
        8
    Nitroethane  
       2018-07-27 10:59:36 +08:00 via Android
    可以看看“深入理解计算机系统”的“链接”一章,讲得很清楚
    LuckyKoala
        9
    LuckyKoala  
       2018-07-27 11:38:17 +08:00 via Android
    @thomaswang https://en.m.wikipedia.org/wiki/Executable_and_Linkable_Format 这是 wiki 上对 elf 文件格式的介绍,至于如何链接的,你可以找找链接相关的文章看看。

    系统学习的话,楼上提到的《深入理解计算机系统》我也很推荐。
    thomaswang
        10
    thomaswang  
    OP
       2018-07-27 11:38:43 +08:00
    @Nitroethane 看完,你没有发现,不需要引入.h 文件理论上也是可以链接的吗,可是为什么不能呢,引入的作用是什么呢
    yksoft1
        11
    yksoft1  
       2018-07-27 11:48:06 +08:00
    不要.h 里的声明的话 C 编译器不知道这个外部符号的类型(返回值),会默认设为 int 并产生警告。C++里面对于函数以外的符号没有声明,直接就是错误了。
    LuckyKoala
        12
    LuckyKoala  
       2018-07-27 11:50:51 +08:00 via Android
    @thomaswang 为什么不能?你怎么写的?

    不包含头文件的话,自己加上需要的声明就可以了。

    加入 “ int printf(const char *format, ...);” 就可以调用 printf
    usufu
        13
    usufu  
       2018-07-27 11:51:28 +08:00
    程序员的自我修养,这本书看完就知道了。
    GeruzoniAnsasu
        14
    GeruzoniAnsasu  
       2018-07-27 12:05:30 +08:00
    @thomaswang 不是 main 函数编译会有个 elf

    每个.c 都会单独编译成待链接的.o 文件,其中包含本文件定义的强弱符号以及需要链接外部的未定义符号,所有未定义符号都会在链接阶段在所有链接文件中查找,并把对应的调用替换成实际函数地址
    如果期望的某个未定义符号没有找到,一般就会报链接错误,但其实也可以用-undefined dynamic_lookup 强制所有未定义符号在运行期动态查找,标准库中的函数“自带这种定义”(不准确)
    从原理上来说,main 函数其实也可以不用写的,只要 elf 文件头指定入口点就足够,但一般程序必须写 main 的原因是,编译器额外链接了某个.o,叫 /crt?+\.o/ 什么的,这个.o 自带一个 main 符号的引用,所以不写 main 链接这个.o 的时候查找 main 符号失败会像上面说的报链接错误,如果用参数选项控制不去链接额外的这个 crt 入口.o,就不一定需要 main 了,你可以再试试上面说的把 main 符号强制在运行期查找,会发现能链接出可执行文件,但这个程序你不 preload 一个带 main 的 lib 是跑不起来的

    除了强弱符号,还有一类未定义符号,用 nm 查看类型是 U,这类符号会在 elf 被加载时由 ld.so 调用 dlresolve 在动态库中查找
    GeruzoniAnsasu
        15
    GeruzoniAnsasu  
       2018-07-27 12:05:48 +08:00
    ------
    emmmm 最后一段忘记删了
    iceheart
        16
    iceheart  
       2018-07-27 12:45:23 +08:00 via Android
    1.编译过程
    把 .c 源代码编译成机器码,名字是.o
    2.链接过程
    把编译好的各个.o 文件链接成一个可执行文件
    3.
    每个.c 编译过程都是独立互不相关的。也就是说,编译器编译 a.c 的时候编译器不知道有 b.c 存在,编译 b.c 的时候编译器不知道有 a.c 的存在。

    基于以上关系说明,问题来了:
    => a.c 里使用了 b.c 里的一个函数,编译器编译 a.c 的时候不知道有 b.c,咋办?

    编译器采取了一个办法:
    1.使用一种约定,来描述 b.c 里边 a.c 要使用的函数,编译的时候根据约定生成调用指令。
    2.让链接程序根据这些约定把 a.o,b.o 链接成可执行文件。
    这个约定是什么?就是.h 头文件里的结构声明和函数声明。
    thomaswang
        17
    thomaswang  
    OP
       2018-07-27 14:51:01 +08:00
    @iceheart 多谢你来解我疑惑,你是很明白的
    a.c 在编译阶段,不需要约定也是可以,每个.o 文件都有一个符号表,里面有自己的函数符号,也有调用的别人的函数符号,当链接的时候,每个.o 文件到其他所有的.o 文件找自己符号表里面的调外部的函数符号,这样就可以了,是吧
    thomaswang
        18
    thomaswang  
    OP
       2018-07-27 15:10:11 +08:00
    @iceheart

    大佬,你人在上海不,请你吃顿饭啊,顺便和你聊聊技术和人生
    zuoxiaomo
        19
    zuoxiaomo  
       2018-07-27 15:55:03 +08:00
    @thomaswang @iceheart 建议见面后互相给对方起一个名字。。。
    thomaswang
        20
    thomaswang  
    OP
       2018-07-27 17:34:49 +08:00
    @zuoxiaomo 何意
    j5shi
        21
    j5shi  
       2018-07-27 18:31:10 +08:00 via iPhone
    谁说必须 include 那个头文件,其实有很多方法可以绕开
    iceheart
        22
    iceheart  
       2018-07-27 19:54:18 +08:00 via Android
    说头文件是接口约定,只是一种方便理解的说法。这些东西完全可以复制到.c 里边,然后扔掉头文件。
    但是这个约定还是在的,貌似大家都管他叫叫 ABI。
    thomaswang
        23
    thomaswang  
    OP
       2018-07-27 21:00:36 +08:00
    @iceheart 吃饭的事 ok 哇,我就是想找个智者聊聊,我最近在职看机会
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   966 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 20:14 · PVG 04:14 · LAX 12:14 · JFK 15:14
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.