主頁 > 軟體設計 > 完全自學C(干貨) —— 編譯與鏈接

完全自學C(干貨) —— 編譯與鏈接

2021-08-05 08:03:32 軟體設計

目錄

一,翻譯環境

編譯器

聯結器

二,執行環境

三,預處理詳解

預定義符號

#define

#

##

帶副作用的宏引數

宏和函式的對比

#undef

命令列定義

條件編譯

檔案包含

其他預處理指令


在ANSI C的任何一種實作中,存在兩個不同的環境

  • 翻譯環境,將源代碼轉換為可執行的機器指令;
  • 執行環境,用于實際執行代碼;

一,翻譯環境

  • 每個源檔案通過編譯程序分別轉換成目標檔案;
  • 每個目標檔案由聯結器捆綁在一起,形成一個單一而完整的可執行程式;
  • 聯結器同時也會引入標準C函式庫中任何被該程式用到的函式,且可搜索程式員個人的程式庫,將其需要的函式也鏈接到程式中;

編譯器

  • 編譯 = 預編譯(預處理) + 編譯 + 匯編;
  • cl.exe,是Microsoft C/C++編譯器;

預編譯/預處理(文本操作)

  • #include,完成了頭檔案的包含;
  • #define,定義的符號和宏的替換;
  • 洗掉注釋;

編譯

  • 把C語言代碼,轉換為匯編代碼;
  • 語法分析;
  • 詞法分析;
  • 語意分析;
  • 符號匯總;

《編譯原理》

匯編

  • 把匯編代碼轉換為機器指令(二進制指令);
  • 匯編后生成.obj檔案(elf格式);
  • 生成符號表;

聯結器

  • 鏈接,把多個目標檔案和鏈接庫鏈接生成可執行程式(elf格式);
  • link.exe;

《程式員的自我修養》

二,執行環境

程式執行程序

  • 程式必須載入記憶體中;作業系統環境中,一般由作業系統完成;獨立環境中,必須手動安排,也可能通過執行代碼置入只讀記憶體來完成;
  • 開始執行程式,接著呼叫main函式;
  • 開始執行程式代碼,此時程式使用一個運行時堆疊,存盤函式的區域變數和回傳地址;程式同時也可使用靜態記憶體,存盤靜態記憶體中的變數在程式的整個執行程序中一直保留它們的值;
  • 終止程式,正常終止main函式,或意外終止;

三,預處理詳解

預定義符號

  • C語言已預先定義好的內置符號;
  • 可用于日志資訊,以便于除錯等;
__FILE__ //進行編譯的源檔案
__LINE__ //檔案當前的行號
__DATE__ //檔案被編譯的日期
__TIME__ //檔案被編譯的時間
__STDC__ //如檔案編譯器遵循ANSI C,其值為1,否則未定義(gcc支持、vs不支持)
int main()
{
	printf("%s\n", __FILE__); //F:\VS\Project1\test.c
	printf("%d\n", __LINE__); //1990
	printf("%s\n", __DATE__); //Aug  1 2021
	printf("%s\n", __TIME__); //10:23 : 43
}

#define

#define定義的識別符號

  • 預處理階段時,替換;
  • 末尾建議不要加(;),否則多一個空陳述句或語法錯誤;
#define MAX 1000 //可替換數值
#define reg register //可替換關鍵字
#define do forever for(;;) //可替換一段陳述句
#define CASE break;case //可替換一段代碼
//可替換多行代碼(\續行符)
#define DEBUG_PRINT printf("file:%s\tline:%d\tdate:%s\ttime:%s\n",\
							__FILE__, __LINE__, \
							__DATE__, __TIME__)
	

#define定義宏

  • 允許把引數替換到文本中,稱為宏(macro)或定義宏(define macro);

#define name( parament-list ) stuff

  • parament-list 引數串列,會在stuff中完成替換;
  • 括號()必須緊鄰name;
#define SQUARE(x) x*x

int main()
{
	int ret = SQUARE(4); //先傳參,在替換
	printf("%d", ret); 
}
//結果:16

注:

  • 宏是先替換,在計算的;
  • 對數值運算式進行求值的宏定義,應加上括號,避免在使用宏時由于引數中的運算子和鄰近運算子之間產生歧義;
#define SQUARE(x) x*x

int main()
{
	int ret1 = SQUARE(3 + 1); //3+1*3+1
	int ret2 = 3 * SQUARE(3 + 1); //3*3+1*3+1
	printf("%d %d", ret1, ret2);
}
//結果:7 13
#define SQUARE(x) ((x)*(x))

int main()
{
	int ret1 = SQUARE(3 + 1); //(3+1)*(3+1)
	int ret2 = 3 * SQUARE(3 + 1); //3*((3+1)*(3+1))
	printf("%d %d", ret1, ret2);
}
//結果:16 48

#define替換規則

  • 在呼叫宏時,首先對引數進行檢查,看是否包含任何由#define定義的符號;如果是,它們首先被替換;
  • 替換文本會被插入到程式中原來文本的位置;對于宏,引數名被他們的值替換;
  • 最后,再次對結果檔案進行掃描,看是否包含任何由#define定義的符號,如果是,就重復上述處理程序,
#define M 100
#define MAX(x,y) ((x)>(y)?(x):(y))

int main()
{
	int ret = MAX(10, M);
	return 0;
}

注:

  • 宏引數和#define定義中,可以出現其他#define定義的變數,但是對于宏,不能出現遞回,
  • 當前處理器搜索#define定義的符號的時候,字串常量的內容并不被搜索,
#define M 100

int main()
{
	printf("M"); //此M不會被替換
	return 0;
}

#

  • #宏引數,可把一個宏引數變成對應的字串;
  • 可實作在字串中插入引數;
#define fun(x) #x //即轉換為“x”

int main()
{
	char str[] = fun(abcd);
	printf("%s", str);
	return 0;
}
//結果:abcd
#define print(x, Format) printf("The value of "#x" "Format"!\n", x)

int main()
{
	int num1 = 10;
	float num2 = 20;
	print(num1, "%d"); //printf("The value of ""num1"" %d!\n", num1)
	print(num2, "%f"); //printf("The value of ""num2"" %f!\n", num2)
	return 0;
}
//The value of num1 10!
//The value of num2 20.000000!

##

  • 可把兩邊的符號合成一個符號;
  • 連接后需產生一個合法的識別符號;
#define CAT(x, y) x##y

int main()
{
	char str[] = CAT("ab", "cd");
	int num1 = 10;
	int num2 = CAT(num, 1); 
	return 0;
}

帶副作用的宏引數

  • 當宏引數在宏定義中出現超過一次時,帶有副作用的引數,可能會出現未知風險;
#define MAX(x, y) ((x)>(y)?(x):(y))

int main()
{
	int a = 5;
	int b = 8;
	int ret = MAX(a++, b++); //((a++)>(b++)?(a++):(b++))
	printf("%d %d %d", ret, a, b); //9 6 10
}

宏和函式的對比

宏,通常被用于執行簡單的運算;

宏優勢

  • 用于呼叫函式和函式回傳的代碼,可能會比實際執行小型計算的作業量所需時間更多;所以宏比函式在程式的規模和速度方面更優;
  • 函式的引數必須宣告為特定的型別,所以函式只能在型別合適的運算式上使用;但,宏與型別無關;

宏劣勢

  • 每次使用宏時,會插入到程式中;可能會大幅度增加程式的長度;
  • 宏無法除錯,除錯是在可執行程式后;
  • 宏與型別無關,所以不夠嚴謹;
  • 宏可能帶來運算子優先級的問題,容易出錯;
//宏,可傳型別引數
#define MALLOC(num,type) (type*)malloc(num*sizeof(type))

int main()
{
    //(int*)malloc(10*sizeof(int))
	MALLOC(10, int);
    return 0;
}

注:

  • 命名約定,宏名全部大寫,函式名不全部大寫;

#undef

  • 取消宏定義
#define M 100

int main()
{
	#undef M
	printf("%d", M); //M為未定義的識別符號
	return 0;
}

命令列定義

  • 許多C編譯器,均允許命令列中定義符號,用于啟動編譯程序;
  • 如,在命令列中定義數值長度;

條件編譯

#define M 100

int main()
{
#ifndef N
	printf("%d", M); //如未定義了N,即編譯此陳述句
#endif

#ifdef M
	printf("%d", M); //如定義了M,即編譯此陳述句
#endif
	return 0;
}

檔案包含

#include

  • 可使另一個檔案被編譯;
  • 替換程序為前處理器先洗掉此指令,用包含檔案的內容替換,被包含幾次就替換幾次;

頭檔案包含方式

  • 本地檔案包含,#include “filename”;先在源檔案所在目錄下查找,如該頭檔案未找到,編譯器在像查找庫函式頭檔案一樣,在標準位置查找頭檔案,如在未找到,提示編譯錯誤;
  • 庫檔案包含,#include <filename>;直接去標準路徑下去查找,如果找不到就提示編譯錯誤;

注:

  • linux環境標準頭檔案的路徑,/usr/include;
  • VS環境標準頭檔案的路徑,C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\ucrt;

嵌套檔案包含

  • 會造成多次重復參考頭檔案;
  • 解決方法,條件編譯(在每個頭檔案開頭添加#ifndef...#endif、或#pragma once);

其他預處理指令

  • #error
  • #pragma
  • #line
  • #pragma pack()
  • ...

《高質量C/C++編程指南》

《C語言深度剖析》

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/291912.html

標籤:其他

上一篇:租房子看“肥學”?依托某地圖和爬蟲找房?(python知識學習)

下一篇:2021電賽延期了?電賽該如何準備?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more