专栏首页编程珠玑C语言为什么只需要include<stdio.h>就能使用里面声明的函数?

C语言为什么只需要include<stdio.h>就能使用里面声明的函数?

有人问:C语言为什么只需要include<stdio.h>就能使用里面声明的函数?这是一个看起来非常简单的问题,但是很多初学者,甚至学了很久的人都可能没有搞明白。

为什么包含即可用?

要明白包含即可用的原因,就必须讲到C语言代码是如何变成可执行文件的了,这里可以参考《hello程序是如何变成可执行文件的》。这里使用#include指令,在预编译之后,相当于把文件里面的内容都放到.c中了。

//hello.c
#include<stdio.h>
int main(void)
{
    printf("hello,编程珠玑\n");
    return 0;
}

这一点也很容易验证:

$ gcc -E -o hello.i hello.c

执行完成之后,就可以看到hello.i里面涵盖了stdio.h中所有的内容。所以实际上,你只是在你的.c中声明了这些函数,既然声明了,那么你就可以使用。但是你要想真正用到它,还需要找到它的定义。这是在链接阶段做的事情。

链接的时候,链接器会知道,诶,你这个程序需要printf函数啊?好的,我去libc.so里面找找,看看有没有哈。,巧了,还真有,恭喜你可以用。所以,这是一个,你用了,然后编译器帮你找了,而且还找到了的巧合事件而已。

包含就够吗?

当然不够!

这个事情表面上看起来理所当然。但是有一个非常重要的前提:

  • 编译器默认链接了libc库(或者类似的库)

如果没有这个前提,就不会是包含即可用。

实际上,这一点我已经在《一个奇怪的链接问题》中提到过了。看一下下面的代码:

//pow.c
//来源:公众号【编程珠玑】
//作者:守望先生
#include<stdio.h>
#include<math.h>
int main(void)
{
    double pow(double x, double y);
    double a = 2;
    double c = pow(a,4);
    printf("%f ^ 4 = %f\n",a,c);
    return 0;
}

用下面的命令已经不能直接编译出来了:

$ gcc -o pow pow.c
/tmp/ccnou5WK.o: In function `main':
pow.c:(.text+0x2f): undefined reference to `pow'
collect2: error: ld returned 1 exit status

所以说,并不是包含了就可以用。在这种情况下,你必须告诉它,我要用pow函数,并且你要去math库找,于是,按照下面的方式进行编译链接:

$ gcc -o pow pow.c -lm 

就可以了。(-lm表示需要链接math库)

当然了,对于C++,使用pow函数不用链接math库也是可以的,为什么呢?请移步这里《C++为什么不需要单独链接math库?》。

不包含可以用吗?

那么一定要包含才可以使用吗?并非如此。前面说过了,包含不过是使用里面的声明,既然如何,我们自己声明怎么样?看下面的代码:

//hello.c,没有包含stdio.h
int printf (const char *__restrict __format, ...);
//extern int printf (const char *__restrict __format, ...);
int main(void)
{
    printf("hello,编程珠玑\n");
    return 0 ;
}

同样可以好好运行,因为你可以自己声明或者指定为外部声明。不过这样不建议,因为一旦出现自己声明的与实际的不符合,就可能导致意料不到的事情发生。

总结

stdio.h里面的函数,包含即可用,只是巧合而已。包含并调用,只是表明你要用,而能不能用,取决于你有没有。通常stdio.h中的函数,基本都在libc库中,因此都可以用。不包含,但是自己声明调用,同样可以用,当然并不推荐这样做。

所以最终决定你能不能用,是要看自己有没有定义以及其他地方有没有定义。

为便于理解,本文不涉及太多具体的编译链接知识,有兴趣的可以自行扩展。

本文分享自微信公众号 - 编程珠玑(shouwangxiansheng),作者:守望先生

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-05-06

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 32位程序和64位程序这些区别你知道吗?

    我们在编写C/C++程序时,32位程序和64位程序的代码有何区别?如何编写既可以编译成32位程序又可以编译成64位程序的代码?

    编程珠玑
  • 这些C++工程师面试题你都会了吗?

    5、给定三角形ABC和一点P(x,y,z),判断点P是否在ABC内,给出思路并手写代码

    编程珠玑
  • 两步将Linux终端骚操作做成动图!

    输入即可开始记录,默认保存记录文件名为ttyrecord,当然你也可以通过-a参数指定保存文件名:

    编程珠玑
  • 深入理解 hash 结构的另一种形式 —— 开放地址法

    HashMap 无 Java 人不知无 Java 人不晓,它使用开链法处理 hash 碰撞,将碰撞的元素用链表串起来挂在第一维数组上。但是并不是所有语言的字典都...

    老钱
  • 高并发文章浏览量计数系统设计

    https://juejin.im/post/5c3aa3c86fb9a04a0e2d6c9f

    搜云库技术团队
  • Introduce myself

    Good morning,dear professors.My name is Sun ZhaoLi.I am 23 years old.And I come ...

    用户6964327
  • Go无框架开发Web应用

    作为新(网络)时代的编程语言,go本身就具备了web开发的特性,也就是你不需要框架就可以开始写web程序,这比用Python实现更容易。(可以看下之前写的: P...

    the5fire
  • 04-树5 Root of AVL Tree (25分)

    An AVL tree is a self-balancing binary search tree. In an AVL tree, the heights ...

    AI那点小事
  • sed的用法

    sed是一个在linux下很好用的文件处理工具,主要是以行为单位进行处理,可以将数据行进行替换、删除、新增等。

    生信编程日常
  • 修改WordPress登陆文件名wp-login.php,防密码被暴力破解

    最近WordPress界出了条新闻:博客平台Wordpress网站遭遇大规模暴力破解攻击(原文附后)。看到这条消息,我立马到空间后台查看了下,发现确实是有很多来...

    Jeff

扫码关注云+社区

领取腾讯云代金券