V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
thomaswang
V2EX  ›  问与答

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

  •  
  •   thomaswang · Jul 27, 2018 · 3636 views
    This topic created in 2834 days ago, the information mentioned may be changed or developed.

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

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

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

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

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

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

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

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

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

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

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

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

    加入 “ int printf(const char *format, ...);” 就可以调用 printf
    usufu
        13
    usufu  
       Jul 27, 2018
    程序员的自我修养,这本书看完就知道了。
    GeruzoniAnsasu
        14
    GeruzoniAnsasu  
       Jul 27, 2018
    @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  
       Jul 27, 2018
    ------
    emmmm 最后一段忘记删了
    iceheart
        16
    iceheart  
       Jul 27, 2018 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
       Jul 27, 2018
    @iceheart 多谢你来解我疑惑,你是很明白的
    a.c 在编译阶段,不需要约定也是可以,每个.o 文件都有一个符号表,里面有自己的函数符号,也有调用的别人的函数符号,当链接的时候,每个.o 文件到其他所有的.o 文件找自己符号表里面的调外部的函数符号,这样就可以了,是吧
    thomaswang
        18
    thomaswang  
    OP
       Jul 27, 2018
    @iceheart

    大佬,你人在上海不,请你吃顿饭啊,顺便和你聊聊技术和人生
    zuoxiaomo
        19
    zuoxiaomo  
       Jul 27, 2018
    @thomaswang @iceheart 建议见面后互相给对方起一个名字。。。
    thomaswang
        20
    thomaswang  
    OP
       Jul 27, 2018
    @zuoxiaomo 何意
    j5shi
        21
    j5shi  
       Jul 27, 2018 via iPhone
    谁说必须 include 那个头文件,其实有很多方法可以绕开
    iceheart
        22
    iceheart  
       Jul 27, 2018 via Android
    说头文件是接口约定,只是一种方便理解的说法。这些东西完全可以复制到.c 里边,然后扔掉头文件。
    但是这个约定还是在的,貌似大家都管他叫叫 ABI。
    thomaswang
        23
    thomaswang  
    OP
       Jul 27, 2018
    @iceheart 吃饭的事 ok 哇,我就是想找个智者聊聊,我最近在职看机会
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2642 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 47ms · UTC 11:27 · PVG 19:27 · LAX 04:27 · JFK 07:27
    ♥ Do have faith in what you're doing.