5.編譯鏈接和宏**

1. 宏(考察很多)-要求輕松實現宏,很容易出錯

#define 機制包括了一個規定,允許把參數替換到文本中,這種實現通常稱為宏或定義宏。

下面是宏的聲明方式:

#define name(參數列表) 內容

參數列表的左括號必須與name緊鄰,如果兩者間存在空白,參數列表就會被解釋成內容的一部分。

#define SQUARE(x) x*x

這個宏接收一個參數x,如果寫SQUARE(5),預處理器就會用5*5這個表達式替換SQUARE(5)。

但是這個宏存在一個問題:

#define SQUARE(x) x*xint main()
{int a = 5;printf("%d\n", SQUARE(a+1));return 0;
}

我們想象中應該是6×6=36,但是實際結果居然是11,為什么呢?

SQUARE(a+1) 實際上被替換成了 a+1*a+1,并不是(a+1)*(a+1),所以結果是11。

應該在宏定義上加上兩個括號:

#define SQUARE(x) (x)*(x)

如果是這樣一個宏,我們吸取經驗在每個x上加上括號:

#define DOUBLE(x) (x)+(x)int main()
{int a = 5;// printf("%d\n", SQUARE(a+1));printf("%d\n", 10*DOUBLE(a));return 0;
}

我們想象中是10×10=100,但是實際上是55,我們展開替換DOUBLE,實際上是10*(5)+(5)

先算10*5 = 50, 最后再+5,等于55。

所以我們要在外面也加上括號。

#define DOUBLE(x) ((x)+(x))

所以用于對數值表達式進行求值的宏定義都應該用這種方式加上括號,避免在使用宏時由于參數中的操作符或鄰近操作符之間不可預料的相互作用。

當宏參數在宏的定義中出現超過一次時,如果參數帶有副作用,那么在使用這個宏的時候就可能出現危險,導致不可預測的后果。副作用就是表達式求值的時候出現永久性的效果

x++就是帶有副作用

#define MAX(x, y) ((x)>(y)?(x):(y))int x = 5;int y = 8;int z = MAX(x++, y++);printf("%d %d %d\n", x, y, z);

預處理器處理后的結果是:

z = ((x++) > (y++) ? (x++) : (y++))

?(x++) > (y++) 都會走,走完x=6, y=9,然后走y++,z=9,y=10,最后的結果就是6,10,9。

某筆試題:

寫一個宏,計算結構體中某變量相對于首地址的便宜,并給出說明

#define OFFSET_OF(type, member) ((size_t)&(((type *)0)->member))

說明:

  1. type: 結構體的類型。

  2. member: 結構體中的成員變量。

  3. ((type *)0): 將地址 0 強制轉換為指向 type 類型的指針。這相當于假設結構體的首地址是 0

  4. &(((type *)0)->member): 獲取成員變量 member 的地址。由于結構體的首地址是 0,這個地址就是成員變量相對于結構體首地址的偏移量。

  5. (size_t): 將偏移量轉換為 size_t 類型,通常用于表示內存地址或偏移量的大小。

struct example 
{int a;char b;double c;
};int main() 
{printf("Offset of 'a': %zu\n", OFFSET_OF(struct example, a));printf("Offset of 'b': %zu\n", OFFSET_OF(struct example, b));printf("Offset of 'c': %zu\n", OFFSET_OF(struct example, c));return 0;
}
Offset of 'a': 0
Offset of 'b': 4
Offset of 'c': 8

2. 編譯鏈接的過程(考的不多)

1. 預處理(Preprocessing)

預處理是編譯過程的第一步,主要處理源代碼中的預處理指令(以 # 開頭的指令)。

主要任務:

  • 宏展開:將所有的宏定義展開。

  • 頭文件包含:將 #include 指定的頭文件內容插入到源文件中。

  • 條件編譯:根據 #if#ifdef 等條件編譯指令,選擇性地包含或排除代碼。

  • 刪除注釋:刪除源代碼中的注釋。

輸入輸出:

  • 輸入:.c 源文件。

  • 輸出:.i 預處理后的文件。

gcc -E main.c -o main.i

2. 編譯(Compilation)

編譯階段將預處理后的代碼轉換為匯編代碼。

主要任務:

  • 詞法分析:將源代碼分解為 token(如關鍵字、標識符、運算符等)。

  • 語法分析:根據語法規則構建抽象語法樹(AST)。

  • 語義分析:檢查語義是否正確(如類型檢查)。

  • 代碼優化:對代碼進行優化。

  • 生成匯編代碼:將高級語言代碼轉換為目標機器的匯編代碼。

輸入輸出:

  • 輸入:.i 預處理后的文件。

  • 輸出:.s 匯編文件。

gcc -S main.i -o main.s

3. 匯編(Assembly)

匯編階段將匯編代碼轉換為機器代碼(目標文件)。

主要任務:

  • 將匯編代碼翻譯為機器指令。

  • 生成目標文件(通常是 .o.obj 文件),包含機器代碼和符號表。

輸入輸出:

  • 輸入:.s 匯編文件。

  • 輸出:.o 目標文件。

gcc -c main.s -o main.o

4. 鏈接(Linking)

鏈接階段將多個目標文件和庫文件合并為一個可執行文件。

主要任務:

  • 符號解析:解析目標文件中的未定義符號(如函數和變量)。

  • 地址分配:為代碼和數據分配最終的內存地址。

  • 重定位:根據最終的內存地址調整代碼中的引用。

  • 合并目標文件:將多個目標文件合并為一個可執行文件。

  • 鏈接庫文件:將靜態庫或動態庫鏈接到可執行文件中。

輸入輸出:

  • 輸入:.o 目標文件和庫文件。

  • 輸出:可執行文件(如 a.outmain.exe)。

gcc main.o -o main

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

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

相關文章

如何搭建一個適配微信小程序,h5,app的uni-app項目

在vscode搭建 uni-app 項目(Vue 3 Vite Pinia uView Plus) 一、環境準備 1. 安裝 Node.js 確保已安裝 Node.js(需≥14版本),可通過以下命令檢查版本: node -v2. 安裝 VSCode 從 VSCode 官網 下載并…

Kotlin apply 方法的用法和使用場景

Kotlin apply 方法的用法和使用場景 1. 方法簡介 apply 是 Kotlin 標準庫中的一個擴展函數&#xff0c;用于對對象執行一系列操作&#xff0c;并返回該對象本身。它的語法如下&#xff1a; inline fun <T> T.apply(block: T.() -> Unit): T參數&#xff1a;block 是…

一文解讀python高階功能:匿名函數到魔法方法(__call__)

文章目錄 一、python中匿名方法的使用使用示例注意事項總結 二、匿名函數和魔法方法的結合示例&#xff1a;結合 lambda 和 __call__解釋更復雜的示例 總結 一、python中匿名方法的使用 在 Python 中&#xff0c;匿名方法是通過 lambda 關鍵字定義的&#xff0c;通常稱為 lamb…

云服務器新手配置內網穿透服務(frp)

首先你得有一個公網服務器&#xff0c;有了它你就可以借助它&#xff0c;將自己電腦進行配置內網穿透&#xff0c;讓自己內網電腦也可以異地輕松訪問。網上教程較多&#xff0c;特此記錄我自己的配置&#xff0c;避免迷路&#xff0c;我這里只記錄我自己云服務小白&#xff0c;…

基于STM32的火災報警設備(阿里云平臺)

目錄 前言&#xff1a; 一、項目介紹和演示視頻 二、硬件需求準備 三、硬件框圖 1. 原理圖 2. PCB 四、CubeMX配置 五、代碼框架 前言&#xff1a; 源代碼下載鏈接&#xff1a; https://download.csdn.net/download/m0_74712453/90474701 需要實物的可以私信博主或者…

學習筆記之車票搜索為什么用Redis而不是ES?

在文章正式開始前&#xff0c;大家打開 12306.cn 搜索一趟列車&#xff0c;根據搜索條件判斷&#xff0c;數據搜索技術使用 ElasticSearch 或者其它搜索技術是否合適&#xff1f; 這里我先把答案說下&#xff0c;12306 車票搜索用的是 Redis &#xff0c;而不是大家常用的 Ela…

揭秘AI:機器學習與深度學習的奧秘

文章目錄 機器學習與深度學習1. 什么是人工智能&#xff1f;2. 機器學習、深度學習和人工智能又是什么關系&#xff1f;3. 人工智能解決了什么問題&#xff1f;為什么需要人工智能&#xff1f;4. 機器學習、深度學習常用術語1&#xff09;模型2&#xff09;數據集3&#xff09;…

【具體場景實踐】使用存儲過程查數據全流程+自動調度

文章目錄 場景設計場景描述:公司員工管理系統需求1. 創建數據庫和表2. 插入測試數據3. 復雜存儲過程4. 調用存儲過程5. 結果示例6. 細節優化存儲過程總結7. 自動定期執行存儲過程7.1 啟用 MySQL 事件調度器7.2 創建定時任務(每天凌晨 2 點自動執行)7.3 查看和管理事件1?? …

【ubuntu】——wsl中使用windows中的adb

一、引言 在 Windows Subsystem for Linux&#xff08;WSL&#xff09;環境下工作時&#xff0c;有時需要使用 Android Debug Bridge&#xff08;ADB&#xff09;工具與 Android 設備進行交互。通過特定設置&#xff0c;能夠在 WSL 中便捷地調用 Windows 系統中已安裝的 ADB&a…

Centos離線安裝gcc

文章目錄 Centos離線安裝gcc1. gcc是什么&#xff1f;2. gcc下載地址3. gcc的安裝4. 安裝結果驗證 Centos離線安裝gcc 1. gcc是什么&#xff1f; GCC&#xff08;GNU Compiler Collection&#xff09;是 GNU 項目下的開源編譯器套件&#xff0c;主要用于將 C、C 等編程語言的源…

JAVA中的多態性以及它在實際編程中的作用

JAVA中的多態性以及它在實際編程中的作用&#xff1f; 在Java中&#xff0c;多態性是指一個對象可以具有多種形態。它主要體現在兩個方面&#xff1a;編譯時多態和運行時多態。 1.編譯時多態 編譯時多態通過方法重載&#xff08;Overloading&#xff09;來實現。方法重載是指…

NetLink內核套接字案例分析

一、基礎知識 Netlink 是 Linux 系統中一種內核與用戶空間通信的高效機制&#xff0c;而 Netlink 消息是這種通信的核心載體。它允許用戶態程序&#xff08;如網絡配置工具、監控工具&#xff09;與內核子系統&#xff08;如網絡協議棧、設備驅動&#xff09;交換數據&#xff…

批量壓縮與優化 Excel 文檔,減少 Excel 文檔大小

當我們在 Excel 文檔中插入圖片資源的時候&#xff0c;如果我們插入的是原圖&#xff0c;可能會導致 Excel 變得非常的大。這非常不利于我們傳輸或者共享。那么當我們的 Excel 文件非常大的時候&#xff0c;我們就需要對文檔做一些壓縮或者優化的處理。那有沒有什么方法可以實現…

基于深度學習的多模態人臉情緒識別研究與實現(視頻+圖像+語音)

這是一個結合圖像和音頻的情緒識別系統&#xff0c;從架構、數據準備、模型實現、訓練等。包括數據收集、預處理、模型訓練、融合方法、部署優化等全流程。確定完整系統的組成部分&#xff1a;數據收集與處理、模型設計與訓練、多模態融合、系統集成、部署優化、用戶界面等。詳…

保姆級離線TiDB V8+解釋

以前學習的時候還是3版本&#xff0c;如今已經是8版本了 https://cn.pingcap.com/product-community/?_gl1ujh2l9_gcl_auMTI3MTI3NTM3NC4xNzM5MjU3ODE2_gaMTYwNzE2NTI4OC4xNzMzOTA1MjUz_ga_3JVXJ41175MTc0MTk1NTc1OC4xMS4xLjE3NDE5NTU3NjIuNTYuMC41NDk4MTMxNTM._ga_CPG2VW1Y4…

spark實驗2

一.實驗題目 實驗所需要求&#xff1a; centos7虛擬機 pyspark spark python3 hadoop分布式 統計歷屆春晚的節目數目 統計各個類型節目的數量&#xff0c;顯示前10名 統計相聲類節目歷年的數目。 查詢每個演員在春晚上表演節目的數量。 統計每年各類節目的數量&#xff0…

學習文章:Spring Boot 中如何使用 `@Async` 實現異步處理

文章目錄 學習文章&#xff1a;Spring Boot 中如何使用 Async 實現異步處理 一、什么是 Async&#xff1f;優點&#xff1a; 二、Spring Boot 中啟用 Async1. 啟用異步支持2. 配置線程池&#xff08;可選&#xff09;3. 使用 Async 注解4. 調用異步方法 三、Async 的進階用法1.…

Manus:成為AI Agent領域的標桿

一、引言 官網&#xff1a;Manus 隨著人工智能技術的飛速發展&#xff0c;AI Agent&#xff08;智能體&#xff09;作為人工智能領域的重要分支&#xff0c;正逐漸從概念走向現實&#xff0c;并在各行各業展現出巨大的應用潛力。在眾多AI Agent產品中&#xff0c;Manus以其獨…

Git Fast-forward 合并詳解:原理、場景與最佳實踐

在使用 Git 進行團隊協作時&#xff0c;我們經常需要合并分支。合并方式有很多種&#xff0c;其中 Fast-forward&#xff08;快速合并&#xff09; 是一種最簡單且無沖突的合并方式。本文將詳細介紹 Fast-forward 的原理、適用場景、常見問題及最佳實踐。 一、Fast-forward 合并…

命令行重啟Ubuntu軟件

我是用Todesk遠程桌面&#xff0c;如果卡死的時候&#xff0c;只能通過ssh連接命令行。于是&#xff0c;就有了如標題所示的需求。 首先&#xff0c;我們看一下todesk在系統里叫什么名字&#xff1a; systemctl list-unit-files | grep -i todesk看到發現是"todeskd.serv…