C語言為什麼只需要include就能使用裡面聲明的函數?

2021-03-02 編程珠璣

‍‍

有人問: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裡面找找,看看有沒有哈。,巧了,還真有,恭喜你可以用。所以,這是一個,你用了,然後編譯器幫你找了,而且還找到了的巧合事件而已。包含就夠嗎?這個事情表面上看起來理所當然。但是有一個非常重要的前提:

//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庫找,於是,按照下面的方式進行編譯連結:

不包含可以用嗎?那麼一定要包含才可以使用嗎?並非如此。前面說過了,包含不過是使用裡面的聲明,既然如何,我們自己聲明怎麼樣?看下面的代碼:

//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庫中,因此都可以用。不包含,但是自己聲明調用,同樣可以用,當然並不推薦這樣做。所以最終決定你能不能用,是要看自己有沒有定義以及其他地方有沒有定義。為便於理解,本文不涉及太多具體的編譯連結知識,有興趣的可以自行擴展。

關注公眾號【編程珠璣】,獲取更多Linux/C/C++/數據結構與算法/計算機基礎/工具等原創技術文章。後臺免費獲取經典電子書和視頻資源

堅持原創

相關焦點

  • #include,為什麼C語言代碼開頭都有這一行?
    點擊上方「C
  • 不#include 能調用printf嗎?解密extern關鍵字的奧秘!
    我們在第一節的時候,標準的寫法是 #include <stdio.h>
  • C語言之#include用法詳解
    學習Linux C,必須要理解include,只要弄清以下幾個問題,就能完全理解include了!1.
  • 為什麼C語言開頭都是這個?
    #include,為什麼C語言開頭都是這個?#include<stdio.h>是在程序編譯之前要進行處理的的內容,因此也稱為預處理命令。先介紹下include,include是一個計算機專業術語,指C/C++中包含頭文件命令,用於將指定頭文件嵌入源文件中。而stdio.h則是C語言編譯系統提供的一個文件名,stdio是"standard input & output"的縮寫,即有關標準輸入輸出的信息。
  • 《C語言入門指南》中篇
    函數的定義函數的形式:返回類型 函數名(形參列表){ 執行語句...; // 函數體 return 返回值; // 可選}頭文件在實際的開發中,我們往往需要在不同的文件中,去調用其它文件的定義的函數,比如hello.c中,去使用myfuns.c 文件中的函數,如何實現?
  • ​【轉載】字符轉數字:C語言裡atoi函數和它的童鞋
    了解途徑1、百度、技術論壇2、Linux環境下使用man查詢man atoi需包含頭文件#include <stdlib.h>函數原型在這裡需要說明一下,如果你不使用sprintf函數去代替itoa函數,可以通過其他途徑實現,比如說把運行環境改成Windows環境,就可以了。
  • C語言const 關鍵字
    我們看看下面這個代碼#include "stdio.h"#include "stdint.h"void printTwo(uint32_t a, uint64_t b);void printTwo(const uint32_t a, const uint64_t b) { printf("%d %d\n", a, b);}int
  • 《C語言入門指南》上篇
    #include <stdio.h> //引入頭文件void main(){ // printf 是一個函數,需要引入頭文件才能使用 // printf 是在<stdio.h> 下的一個文件,需要引入它才行 printf("hello world!")
  • 深入淺出剖析C語言函數指針與回調函數(一)
    這個開發者角色就很多了,可以是自己公司的核心開發人物,也可以是別的工作的外包商的人物,這時候,他作為一個開發者的角色完完全全可以將add_value實現的add_ret這個函數封裝起來並且加密,然後扔一個.so或者.a給用戶,那麼用戶就看不到具體add_ret的實現內容,用戶只需要開發者給他提供一個.h和.so即可,這樣,作為開發者,他就將他實現的函數功能給保密了。
  • 學校裡學不到的C語言教程之4:奇怪的第一行代碼與重複的函數名
    當我們學習 C 語言一定時間後,一定會很熟悉在編寫各種功能代碼邏輯之前一開始就會要 include 一堆的 h 頭文件。例如我們大家都知道的 "#include <stdio.h>"。漸漸的我們會知道,有些功能是要再引入其他的庫才能使用的。
  • C語言頭文件被include後都發生了什麼?為何不能在頭文件定義變量
    頭文件是C語言的一個重要組成部分,這種類型的文件名一般以 .h 結尾,h 表示 header,因此被稱為「頭文件」。頭文件裡一般存放公開的函數原型,數據類型等內容,其他模塊需要使用這些函數或者數據類型時,只需包含相應頭文件即可。
  • 帶你走進C語言,簡單C程序和完整C程序的組成
    初學C語言你第一個代碼肯定是「hello word」這個簡單的C,#include <stdio.h>Int main(){Printf(「HELLO,WORD\n」);#include <stdio.h>:這是#incldude的指令,它被稱為頭文件或者是首文件的意思,在這個裡面「#」代表的是預處理命令,#include是使用頭文件的指令,stdio.h是一個函數庫,初學你只需要知道就行,這在之後我們會在進行一個學習。
  • C語言中的scanf函數
    #include <stdio.h>main(){ int n = 5; char c[n]; for(int i = 0; i < n; i++)  c[i] = scanf("%c",&c[i]);  printf(c);return
  • 程式設計師應如何理解include
    請注意,接下來是重點:預編譯的工作非常簡單,預編譯器找到源文件中#include指定的文件,然後copy這些文件的內容並粘貼到#include這一行所在的位置。假設在源文件a.c的第一行有一句#include <stdio.h>,那麼預編譯器怎麼處理?
  • 深度剖析C語言的main函數
    #include <stdio.h>int main(){    printf("Hello world");}同時,需要說明的是return的返回值會進行 類型轉換,比如:若return 1.2 ;會將其強制轉換為1,即真正的返回值是1,同理,return 『a』
  • 《C語言入門指南》完結篇
    **存全部清零,而是程序放棄對它的使用權限,後面的代碼可以使用這塊內存3、C 語言不支持在調用函數時返回局部變量的地址,如果確實有這樣的需求,需要定義局部變量為 static 變量函數指針1、 一個函數總是佔用一段連續的內存區域,函數名在表達式中有時也會被轉換為該函數所在內存區域的首地址
  • C語言標準庫系列之 -
    C 標準庫的 ctype.h頭文件提供了一些函數,可用於測試和映射字符。這些函數接受 int作為參數,它的值必須是 EOF 或表示為一個無符號字符。如果參數 c 滿足描述的條件,則這些函數返回非零(true)。如果參數 c 不滿足描述的條件,則這些函數返回零。
  • C語言入門
    這些都可以用來編寫C語言程序。2、什麼是編譯器通過編輯器寫出的代碼只是源程序的文本文件,必須經過編譯之後才可以在電腦上運行。常用的編譯器有:microsoft C++Compiler、gcc等。3、什麼是集成開發環境(IDE)集成開發環境就是為程序開發提供的環境應用軟體,裡面集成了編輯器和編譯器。
  • C語言中getchar()、getche()和getch()函數的區別
    ===Tips:點擊上方 藍字 關注並查看歷史消息=== getchar()函數是C語言專門為輸入單個字符而提供的
  • c語言指針與字符數組
    ,字符串的使用在C語言中也是非常重要的,常常會遇到一些操作,如字符串的修改、拷貝、字符串長度等,在物聯網的應用中也尤為突出,物聯網應用中所用的模組,大多是需要使用AT指令的,這就需要對字符串的操作。我們列印出來看一下#include<stdio.h>int main(){ char c[] = "hello"; peintf("size of c = %d\n",sizeof(c)); return 0;}我們列印出來的結果是6明明裡面是5個字符