C語言多文件編譯鏈接為1個可執行文件的簡單原理

參考1:C header files and compilation/linking
參考2:計算機系統基礎(一)袁春風 (符號鏈接部分)

我們現在有一個簡單的工程,有這么幾個文件

1.t1.h

extern int x;void tt();
  1. t1.c
#include "t1.h"int x;void tt(){x = 100;
}
  1. main.c
#include <stdio.h>
#include "t1.h"int main(){tt();printf("x = %d\n",x);return 0;
}

現在,我們依次來看一下

  • 頭文件 和 C源文件 如何聯系在一起
  • 多文件如何變成1個可執行目標文件

預處理,組合頭文件與包含它的C源文件

在32位Linux系統中。

我們執行gcc -E t1.c -o t1.i,對源程序進行預處理,將頭文件包含進來。

所謂預處理,就是將預編譯命令處理掉,比如,把頭文件的內容拷貝到C源文件中,因此,我們就能夠理解.h和.c文件中全部變量的關系了,我們看一下生成的文件內容

# 1 "t1.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "t1.c"
# 1 "t1.h" 1
extern int x;void tt();
# 2 "t1.c" 2int x;void tt(){x = 100;
}

可以看到,開始有一些記錄的信息,看起來應該是注釋,是一些說明性信息。

然后我們看到,之前頭文件的內容在這個文件里面了。如果你把它近似地看為一個源文件,你就能大概理解全局變量聲明和定義,以及不要重復定義等。

對于main.c文件的預處理,道理是一樣的

然后,我們將t1.imain.i分別生成其可重定位目標文件,最后鏈接起來,就生成了可執行目標文件。

gcc -c t1.i -o t1.o				# 生成可重定位目標文件
gcc -c main.i -o main.o			# 生成可重定位目標文件
gcc -o main main.o t1.o			# 鏈接,生成可執行目標文件
./main								# 運行可執行目標文件

最后,需要說明

對于函數的聲明(頭文件聲明、外部聲明、內部聲明)與定義(內部定義、外部定義)

在c源文件中進行函數聲明,然后使用它,在鏈接的時候,這個聲明的函數(但未在該文件中定義)鏈接器會自動在其他的可重定位目標文件中尋找其定義,如果找到了就聯系起來,否則就報錯。這就意味著,所有的C源文件中的函數默認都是全局的,你可以在模塊1聲明函數a,然后調用它,在模塊2中定義函數a,之后將兩個模塊鏈接起來生成可執行文件。

好吧,每次都聲明很麻煩,所以,我們在模塊2對應的頭文件聲明,然后模塊1引入頭文件,這樣,我們就能夠進行和之前一樣的操作了,你知道的,頭文件在預處理的時候會被包含進入源文件,這樣,其實達到了和我們手動聲明一樣的效果。

如果你不想讓函數是全局的,那就加上static,這樣,只有該源文件可以使用它了!

對于變量的定義與初始化
  • 局部變量:只能在其{ }內使用
  • static變量:只能在本模塊內使用
  • 全局變量:全部都可以用
    • 本模塊定義
    • 外部定義extern

對于本模塊使用的,沒得說,就是只有這個源文件能夠使用這個變量!

只討論全局變量

  1. extern變量,不要初始化,只聲明即可!
  2. 任何初始化的全局變量,全局范圍內只能初始化1次,可以定義n次,但是最終內存空間中的,就是初始化的內個變量的位置,只有一個!
  3. 頭文件一般包含函數聲明,全局變量的定義似乎沒必要,或者說,不要在頭文件定義全局變量
  4. 全局變量最好少出現,出現一次也就夠了,在不同編譯器下,要求不一樣。少用。 比如在Linux的gcc下,不同C源文件,可以出現多個int x弱符號,但是Windows的VS下就不允許。

因此說,使用全局變量,最好

  • 初始化全局變量
  • 只定義一次
  • 其他的模塊如果要用,就聲明extern表示引用外部定義,而不是再聲明一個弱符號(不是不行,但是這樣的話,gcc能用,VS就不能用)
  • 不要在頭文件定義全局變量(頭文件被多次包含的話…得定義多少次,恐怖)

注意

函數有

  1. 是否聲明
  2. 是否定義
  3. 是否使用

對于任何模塊,必須先聲明,再使用,聲明之后可以不定義,因為可以外部定義,但是被聲明被使用的函數,必須有其定義。 要是聲明之后沒有使用也沒有定義,其實也行,但是最好不要這樣干,太奇怪了!

對于同一模塊,先定義再使用也可以,但是不能先使用再定義,順序很重要。


變量有

  • 是否定義
  • 是否初始化
  • 是否使用

然后,還涉及到不同模塊之間…

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/384620.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/384620.shtml
英文地址,請注明出處:http://en.pswp.cn/news/384620.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java讀寫二維數組到文件

1. 創建文件 使用了File類中的createrNewFile方法。 public static boolean createFile(String filename){try{File file new File(filename);file.createNewFile();return true;} catch (IOException e) {e.printStackTrace();return false;}}查閱文檔可知&#xff0c;若文件…

如何掌握java API的方法

如何掌握方法的使用&#xff1f; 查文檔文檔不一定看得懂&#xff0c;親自試一試就知道了&#xff01; 這倆方法沒啥好說的&#xff0c;自己體會即可&#xff01; 注意&#xff01;先看原版英文文檔&#xff0c;然后自己試一試&#xff0c;就能更加理解了&#xff0c;然后&a…

Leetcode1512. 好數對的數目 抽出本質原型 利用范圍條件

解法1&#xff1a;暴力枚舉 class Solution {public int numIdenticalPairs(int[] nums) {int count 0;for(int i 0;i < nums.length; i){for(int j i 1; j < nums.length; j){if(nums[i] nums[j])count;}}return count;} }沒啥可說的&#xff0c;就是小學數學問題…

leetcode面試題 10.01. 合并排序的數組

直接排序 直接使用Java已有的方法進行排序&#xff0c;這一招…大意了&#xff01; 這題簡單&#xff0c;就是個基本的排序&#xff0c;后面難題&#xff0c;可能這只是一小步&#xff0c;內個時候直接用排序算法比較合適&#xff0c;這個不合適。。 class Solution {public…

IA-32 Architecture: the function of segment regitster(CS DS SS ES)

對于IA-32架構&#xff0c;與8086不同&#xff0c;段寄存器不再是像以前一樣&#xff0c;直接作為段基址&#xff0c;因為32位的寄存器直接就可以表示4GB大小&#xff0c;不需要再偏移&#xff0c;因此段寄存器的含義也發生了相應的變化。 在IA-32架構里&#xff0c;段寄存器是…

x86異常處理與中斷機制(1)概述中斷的來源和處理方式

參考《計算機組成》&#xff08;北京大學 MOOC&#xff09; 1 異常與中斷的來源&#xff08;為什么需要中斷&#xff09; 首先&#xff0c;說明一下異常和中斷這兩個概念。 它們兩個唯一的區別&#xff0c;就是&#xff0c;沒有什么區別。只是不同的地方不同的時間不同的人的…

【C language】typedef的使用:結構體、基本數據類型、數組

typedef基本數據類型 typedef int a; a abc;后面的a abc就等價于int abc typedef結構體 typedef struct a {int a;int b; } abc;abc aaa;對于上述&#xff0c;abc aaa;就等價于struct a aaa; 簡而言之&#xff0c;typedef的本質&#xff0c;就是構建等價關系。 第一個例…

【C language】動態數組的創建和使用

在C語言中&#xff0c;使用malloc函數創建動態數組&#xff0c;使用一個指針指向它&#xff0c;使用下標進行訪問。 unsigned long *a (unsigned long *)malloc(2 * sizeof(int)); a[0] 1000; a[1] 2000; printf("%d %d\n", a[0], a[1]); free(a);上述例子&…

x86異常處理與中斷機制(2)中斷向量表

補充&#xff1a;事件不僅包含中斷和異常&#xff0c;還包含系統調用&#xff0c;這個屬于用戶主動請求的事件。 上一節&#xff0c;只有一個溢出異常&#xff0c;那么&#xff0c;如果很多異常、中斷呢&#xff1f;&#xff08;中斷向量表&#xff09; 另外&#xff0c;之前0…

x86異常處理與中斷機制(3)中斷處理過程

上一節講完了根據中斷類型號找中斷服務程序的過程&#xff0c;現在著重說明一下更加完整的中斷處理過程吧。 本節以8086時代的中斷處理過程為例進行說明&#xff0c;主要分兩大部分 硬件處理軟件處理 需要注意&#xff0c;這不是絕對的&#xff0c;得看實際情況&#xff0c;…

Linux 0.11 內核解析:中斷相關(1)asm.s文件中斷處理分析

0 源代碼 有兩個版本的&#xff0c;一個是帶中文注釋&#xff0c;Intel格式的&#xff1b;一個是不帶注釋是AT&T格式的。 Linux 0.11 中文注釋版 Linux 0.11 源碼&#xff0c;基于《Linux內核完全注釋》趙炯 1 asm.s 文件 我們先假設該文件處理的中斷是無特權過渡的情況…

【精華文】C語言結構體特殊情況分析:結構體指針 / 基本數據類型指針,指向其他結構體

參考鏈接&#xff1a;Structure pointer pointing to different structure instance 注&#xff1a;可以查看此篇的問題和唯一的回復&#xff0c;那是相對正確的&#xff0c;不要看comment&#xff0c;有很多錯誤。 我是拒絕分析這種問題的&#xff0c;因為似乎沒有人會這么亂用…

enum in c language

今天說說C語言中的枚舉。 參考&#xff1a;Enumeration (or enum) in C 1 定義 定義一個枚舉類型很容易&#xff1a; enum aa { a1, a2, a3 };這里 enum是關鍵字aa是枚舉變量&#xff0c;也就是我們自定義類型a1,a2,a3是枚舉成員 然后怎么使用呢&#xff1f; 首先&#…

信號量SIGCHLD的使用,如何讓父進程得知子進程執行結束,如何讓父進程區分多個子進程的結束

本教程基于 Ubuntu 20.10 gcc 10.2.0. 示例程序如果不能正常編譯和執行&#xff0c;說明您系統和工具版本與我的不匹配&#xff0c;請自行查閱資料。 0 概述 先給出該信號的描述&#xff1a; SignalValueDescriptionSIGCHLD17Child status has changed (POSIX). Signal sent …

使用gdb調試多進程程序、同時調試父進程和子進程

參考: [1] GDB debugging multi-process programs [2] Debugging programs with multiple processes 根據這兩篇參考鏈接&#xff0c;完全可以實現使用gdb同時調試父進程和子進程。 接下來說明一下可能遇到的坑 gdb8.1版本有bug&#xff0c;設置完set detach-fork-on off&…

Linux安裝Ncurses庫

參考&#xff1a;How To Install Ncurses Library In Linux 針對Ubuntu說明一下&#xff1a; wget https://ftp.gnu.org/pub/gnu/ncurses/ncurses-6.2.tar.gz&#xff0c;至于最新版本&#xff0c;自己看官網&#xff0c;修改一下版本號即可。tar xzf ncurses-6.2.tar.gzcd nc…

gdb 10.2的安裝

參考 [1] GDB-10.2 [2] README for GDB release 個人系統 Ubuntu20.10。 注意gdb10.2需要c11語法&#xff0c;需要安裝g 下載安裝包wget https://ftp.gnu.org/gnu/gdb/gdb-10.2.tar.xz解壓縮tar -xvzf gdb-10.2.tar.xz進入解壓之后的目錄mkdir buildcd build配置&#xff0c;…

gdb tui的使用

[1] GDB Text User Interface [2] GDB Text User Interface 簡單來說&#xff0c;進入gdb之后&#xff0c;使用ctrl x 2就足夠了。其他細節請參考上述鏈接&#xff0c;選一個就可以。

C語言中信號函數(signal)的使用

先來簡單談談C語言中的信號&#xff08;signal&#xff09; 首先&#xff0c;signal是C語言庫中的函數&#xff0c;它實際上是軟中斷&#xff0c;也就是軟件發出的終端&#xff0c;本質來說&#xff0c;類似于int n。 對于接收到該軟中斷信號的進程&#xff0c;就會停下手頭的…

UNIX哲學

參考&#xff1a; 對比Linux與Windows 使用Linux想要做某些事情的時候&#xff0c;就拆開想&#xff0c;想想我需要哪些功能&#xff0c;需要哪些工具&#xff0c;依次怎么執行&#xff0c;然后用管道建立連接&#xff0c;讓數據依次流過不同的工具&#xff0c;從而得到最終結果…