V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
rookiemaster
V2EX  ›  C

gcc 是怎么找到 system 函数的定义(实现)的?

  •  
  •   rookiemaster · 38 天前 · 1564 次点击
    这是一个创建于 38 天前的主题,其中的信息可能已经有所发展或是发生改变。

    程序如下:

    int main()
    {
        system("echo hello world");
    }
    

    使用 gcc 编译时会警告找不到 system 的声明:

    DoubleHash.c: In function ‘main’:
    DoubleHash.c:3:5: warning: implicit declaration of function ‘system’ [-Wimplicit-function-declaration]
        3 |     system("echo hello world");
          |     ^~~~~~
    

    但是程序仍然可以运行,我想知道编译器是怎么找到 system 的定义的?

    18 条回复    2024-03-23 17:50:50 +08:00
    ho121
        1
    ho121  
       38 天前
    应该是链接 glibc 时找到的,毕竟大部分程序都需要 glibc

    我猜的。
    geelaw
        2
    geelaw  
       38 天前 via iPhone   ❤️ 2
    system 是标准库函数,链接的时侯默认是带标准库的,所以找到了。

    C 语言没有函数重载,不需要复杂的名称修饰,int system(…); 和 int system(char const *); 链接的时候去找的是同一个符号,前者是隐式声明的结果,后者是标准库的声明,故隐式声明的调用可以链接到标准库函数。
    seers
        3
    seers  
       38 天前 via Android
    运行时候会去 ld.conf 找符号
    yolee599
        4
    yolee599  
       38 天前   ❤️ 1
    int add(int a, int b) 和 float add(float a, int b, int c) 的符号是同一个,运行的时候函数会通过栈传递参数。编译的时候是通过符号来链接的,如果在头文件定义了这个函数,类型不匹配编译会出错,而如果头文件不定义,只要符号对上了编译就会成功,只会报警告。
    churchmice
        5
    churchmice  
       38 天前
    推荐你看一下 link & loader
    wodexinhaoleng
        6
    wodexinhaoleng  
       38 天前   ❤️ 1
    问题的方向错了,这个问题你应该问这个警告是从哪来的

    因为 C 语言最初的设计上,函数调用本来就是不需要声明的。只是后面的编译器保留了兼容性
    proxytoworld
        7
    proxytoworld  
       38 天前
    你不熟悉程序编译过程,首先 gcc 会先生成.a 文件,而后进行链接,在链接的时候找到了 system ,所以能编译出来,system 在 libc 里面。如果你使用一个不存在的函数,在链接的时候找不到,就会报错了。
    pagxir
        8
    pagxir  
       38 天前 via Android
    函数声明在 stdlib.h ,实现自然在 libc ,默认都会链接 libc 的。不 include stdlib 直接用 system 大概率在 64 位系统下程序崩溃
    653513754
        9
    653513754  
       38 天前
    推荐你看一下编译之后的汇编,可以清楚的看出来系统是怎么找到 system 的
    ysc3839
        10
    ysc3839  
       38 天前 via Android
    是 implicit-function-declaration ,直接用你给的参数类型作为定义了
    rookiemaster
        11
    rookiemaster  
    OP
       38 天前 via iPhone
    @geelaw 那隐式声明与实际调用的时候参数对不上为啥编译会通过呢
    leonshaw
        12
    leonshaw  
       38 天前
    @rookiemaster 当作隐式函数声明 int system(); 按 ABI 调用就行了
    geelaw
        13
    geelaw  
       38 天前 via iPhone
    @geelaw #2 更正:隐式声明是 int system();

    #11 空白的参数列表的意思是没有提供参数列表信息,并没有对不上。
    nuk
        14
    nuk  
       38 天前
    隐式声明,理论上默认的函数声明会是 int f(void),但是 gcc 实际上传递了参数,而且因为 X64 是 fastcall ,前几个参数用寄存器传递,所以就算参数数量对不上,也不会崩溃。你可以试试这样,一样不会崩溃:
    system("echo hello world");
    system("echo hello world", "echo hello world");
    cnbatch
        15
    cnbatch  
       38 天前
    “隐式声明与实际调用的时候参数对不上”

    不是对不上,而是因为 C 语言的特性。

    在其它编程语言当中(比如 C++、Java 、C# ),int system();表示该函数不接受参数传入。

    但 C 语言不同,int system(); 表示参数情况未知,是个笼统的声明。
    而 int system(void) 才是其它编程语言 int system();的意思。

    这里有比较详细的说明,在页面尾部 Note 那一节,句子以“Unlike in C++”开头:
    https://en.cppreference.com/w/c/language/function_declaration
    rookiemaster
        16
    rookiemaster  
    OP
       38 天前
    @cnbatch 谢谢
    zeroxia
        17
    zeroxia  
       35 天前
    @nuk 到底默认是 `int f()` 还是 `int f(void)`?
    nuk
        18
    nuk  
       35 天前
    @zeroxia int f(),我说错了,不指定参数类型
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1523 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 16:53 · PVG 00:53 · LAX 09:53 · JFK 12:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.