C語言八股---預處理,編譯,匯編與鏈接篇

前言

??從多個.c文件到達一個可執行文件的四步:
??預處理–>編譯–>匯編–>鏈接

預處理

在這里插入圖片描述

??預處理過程就是預處理器處理這些預處理指令(要不然編譯器完全不認識),最終會生成 main.i的文件
主要做的事情有如下幾點:

  • 展開頭文件
  • 展開宏
  • 條件編譯
  • 刪除注釋
  • 添加行號等信息
  • 保留parama預處理指令
  1. 頭文件展開—#include指令
    • #include <sdtio.h> 和 #include “stdio.h”
      對于<> 搜索順序為
      • 通過GCC參數gcc-I指定的目錄(注:大寫的I 讓我們自由指定的)。
      • 通過環境變量CINCLUDEPATH指定的目錄。
      • GCC的內定目錄。
        對于 " "的搜索時順序
      • 項目當前目錄(此時也可以用"…/LED/led.h"方式去搜索)
      • 通過GCC參數gcc-I指定的目錄。
      • 通過環境變量CINCLUDEPATH指定的目錄。
      • GCC的內定目錄
    • 為什么把聲明放在頭文件里
      • 提供一個接口 方便其他文件通過聲明調用對應的函數
      • 當我們的led.c 包含了 led.h的時候 也方便編譯器做類型的檢查
    • 頭文件多次包含會增加可執行文件的體積嗎?
      只要是使用了類似#pragma once 或者#ifndef 多次包含是不會的增加可執行文件的體積的
      同樣的要注意:聲明不會增加可執行文件的體積
  2. 宏展開#define 宏指令
    • 宏定義最小
      #define MIN(x,y) ((x) > (y) ? (y) : (x))
      因為宏只是做了一個替換所以對于如下代碼
        #include <stdio.h>#define MIN(x,y) ((x) > (y) ? (y) : (x))//因為宏只是做了一個替換所以對于如下代碼int main(){int a = 2 ;int b = 5;int c =  MIN(a++,b++);printf("c = %d a = %d b = %d\r\n",c,a,b); // a竟然等于4}

在這里可以用GNU C語法中的一些小技巧操作

        #define MIN(x,y) ({\typeof(x) _x = (x);\typeof(y) _y = (y);\_x > _y ? _x : _y;})
  • 定義一個很大的常數的時候
    ??#define MAX_LONG (100001000010000)UL //指定類型
  • ##連接符
    ??高端用法 看了好多代碼都用這個 但是分析起來亂亂的,大概就是把兩個字母連接到一起
        #define contact(x,y)  (x##y)int bc = 50;printf("bc = %d\r\n", contact(b,c));
  • offset_of與container_of
    之前寫結構體的時候寫過,權當復習一下
    #define offset_of(type, member)     ((size_t)(&((type *)0)->member))
    #define container_of(type,member,ptr) (type *)((size_t) ptr - offset_of(type,member))
    struct student {int height;char * name;
    };
    int main()
    {struct student stu;stu.height = 50;stu.name = "123456";char ** tmp_name = &stu.name;struct student* s = &stu;printf("%p  %p %ld\r\n",s, tmp_name,offset_of(struct student,height));struct student *new_s = container_of(struct student,name,tmp_name);new_s->height = 60;printf("%d\r\n",stu.height);
    }
    

``

  • 宏為什么要用 do {} while(0)
    ??如果去看linux源碼也好還是RTOS等的代碼也好 會有很多時候用到do_while(0) 它的作用是什么呢
    假設我們定義了
    #define MACRO() foo(); bar()
    此時我們寫了這樣的偽代碼
    if (condition)
    MACRO();
    else
    baz();
    // 宏展開后和我們想要的就完全不一樣了 直接就出錯了
    // do {}while(0)可以保證宏作為一個整體執行 此時就可以定義一些局部變量
  1. 條件編譯 #ifdef等指令
  • 條件編譯指令
    在這里插入圖片描述

    正常用的比較多的就是 #ifndef #define #endif這幾個連用
    也有 #defined(VAR_X)之類的

  • #error指令
    如果發生錯誤直接中斷編譯過程
    在這里插入圖片描述

  1. #pragma 指令
    • #pragma pack([n]):指示結構體和聯合成員的對齊方式。
    • #pragma message(“string”):在編譯信息輸出窗口打印自己的文
      本信息。
    • #pragma warning:有選擇地改變編譯器的警告信息行為。
    • #pragma once:在頭文件中添加這條指令,可以防止頭文件多次
      編譯。

編譯

??真要講編譯我也是不配講的 就我們知道這是在干嘛就行了
??編譯就是把.c文件變成匯編文件的過程

  • 編譯過程的6步
    詞法分析 / 語法分析 / 語義分析 / 中間代碼生成 / 匯編代碼生成 / 目標代碼生成
    • 語法錯誤: stynax error: 缺少分號 / {}沒擴住 /
    • 語義錯誤: 類型不匹配 未定義的變量
      最終的結果就是生成.S文件
  • gcc的優化等級 gcc -O
    可以參考
    https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Optimize-Options
  • 交叉編譯
    嵌入式開發板一般是ARM架構 然后PC是x86架構
    通過交叉編譯器進行程序的編譯

匯編

??匯編就是把匯編代碼編程機器碼 也就是比較熟悉的 xx.o文件了
??匯編過程最終會生成以零地址為鏈接起始地址的可重定位目標文件

鏈接

??把.o 文件進行組裝 需要重定位 因為所有的.o文件的開頭都是以0地址開頭的
??鏈接主要分為3個過程:分段組裝、符號決議和重定位

  • 分段組裝
    不太好講 基本就是通過一個腳本把多個文件按照段組合到一起
    在這里插入圖片描述

  • 符號決議
    符號決議的核心就行 如果說變量/函數重名了怎么辦

    • 不允許同時存在兩個相同的強符號
      初始化的全局變量、函數名默認都是強符號,未初始化的全局變量默認是弱符號
      比如
        // b.cint i;   // 未初始化 是弱符號int main() {printf("%d\r\n",i);  //i的值是20}// a.cint i = 20;

__attribute__關鍵字 可以把強符號強行轉換為弱符號 attribute((weak))

  • 使用弱符號的好處
    • 自定義重名函數
      這里在嵌入式里最常見的就是中斷服務函數的弱定義了
      當我們需要重新定義中斷服務函數的時候 只需要保證名字很start.S的名字一致就行,鏈接的時候就知道鏈接到哪里了
      在這里插入圖片描述

    • 檢查該函數是否存在
      // b.c
      #include <stdio.h>
      int global_k;
      char global_i;
      attribute((weak)) void func()
      {
      printf(“這被定義為弱符號了\r\n”);
      }
      int main()
      {
      printf(“%d\r\n”,global_k);
      if(func)
      func(); //調用的是強符號的函數
      return 0;
      }
      // a.c
      #include <stdio.h>
      int global_k = 20;
      int global_i;
      void func()
      {
      printf(“這被定義為強符號了 fun\r\n”);
      }

  • 同樣都是弱符號 誰體積大誰勝出
  • 重定位
    因為要把不同的文件鏈接到一塊 所以位置就會發生變化

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

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

相關文章

用Deepseek寫一個 HTML 和 JavaScript 實現一個簡單的飛機游戲

大家好&#xff01;今天我將分享如何使用 HTML 和 JavaScript 編寫一個簡單的飛機游戲。這個游戲的核心功能包括&#xff1a;控制飛機移動、發射子彈、敵機生成、碰撞檢測和得分統計。代碼簡潔易懂&#xff0c;適合初學者學習和實踐。 游戲功能概述 玩家控制&#xff1a;使用鍵…

面向高質量視頻生成的擴散模型方法-算法、架構與實現【附核心代碼】

目錄 算法原理 架構 代碼示例 算法原理 正向擴散過程&#xff1a;從真實的視頻數據開始&#xff0c;逐步向其中添加噪聲&#xff0c;隨著時間步 t 的增加&#xff0c;噪聲添加得越來越多&#xff0c;最終將原始視頻數據變成純噪聲。數學上&#xff0c;t 時刻的視頻數據與 t…

水下機器人推進器PID參數整定與MATLAB仿真

水下機器人推進器PID參數整定與MATLAB仿真 1. PID控制原理 目標:通過調節比例(P)、積分(I)、微分(D)參數,使推進器輸出力快速穩定跟蹤期望值。傳遞函數(示例):推進器動力學模型可簡化為: [ G(s) = \frac{K}{\tau s + 1} \cdot e^{-Ts} ] 其中:K為增益,τ為時間常…

游戲引擎學習第149天

今日回顧與計劃 在今天的直播中&#xff0c;我們將繼續進行游戲的開發工作&#xff0c;目標是完成資產文件&#xff08;pack file&#xff09;的測試版本。目前&#xff0c;游戲的資源&#xff08;如位圖和聲音文件&#xff09;是直接從磁盤加載的&#xff0c;而我們正在將其轉…

Java函數式接口四部曲之Consumer

Consumer 是一個函數式接口&#xff0c;位于 java.util.function 包中。它表示一個接受單個輸入參數并且不返回任何結果的操作。Consumer 通常用于需要對輸入參數執行某些操作但不產生返回值的場景。 Consumer 接口定義了一個抽象方法&#xff1a;accept(T t)&#xff1a;接受…

ForceMimic:以力為中心的模仿學習,采用力運動捕捉系統進行接觸豐富的操作

25年3月來自上海交大盧策吾教授團隊的論文“ForceMimic: Force-Centric Imitation Learning with Force-Motion Capture System for Contact-Rich Manipulation”。 在大多數接觸豐富的操作任務中&#xff0c;人類會將隨時間變化的力施加到目標物體上&#xff0c;以補償視覺引…

【愚公系列】《Python網絡爬蟲從入門到精通》045-Charles的SSL證書的安裝

標題詳情作者簡介愚公搬代碼頭銜華為云特約編輯&#xff0c;華為云云享專家&#xff0c;華為開發者專家&#xff0c;華為產品云測專家&#xff0c;CSDN博客專家&#xff0c;CSDN商業化專家&#xff0c;阿里云專家博主&#xff0c;阿里云簽約作者&#xff0c;騰訊云優秀博主&…

vulnhub靶場【digitalworld.local系列】的electrical靶機

前言 靶機&#xff1a;digitalworld.local-electrical&#xff0c;IP地址為192.168.10.12&#xff0c;后期因為卡頓&#xff0c;重新安裝&#xff0c;ip地址后面為192.168.10.11 攻擊&#xff1a;kali&#xff0c;IP地址為192.168.10.6 kali采用VMware虛擬機&#xff0c;靶機…

macos 程序 運行

sudo xattr -r -d com.apple.quarantine [/Applications/Name]使用stow 管理配置文件

多視圖幾何--結構恢復--三角測量

三角測量 1. 核心公式推導 假設兩個相機的投影矩陣為 P P P 和 P ′ P P′&#xff0c;對應的匹配圖像點(同名點)為 ( u , v ) (u, v) (u,v) 和 ( u ′ , v ′ ) (u, v) (u′,v′)&#xff0c;目標是求解三維點 X [ X x , X y , X z , 1 ] T X [X_x, X_y, X_z, 1]^T X…

共享內存的原理和創建

目錄 共享內存的原理 共享內存的創建 代碼實現創建 共享內存的管理指令 我們今天來學習共享內存&#xff01;&#xff01;&#xff01; 共享內存的原理 兩個進程同時使用內存中開辟的共享空間進行通信就是建立并使用共享內存進行進程間的通信。System V 共享內存&#xf…

3.10[A]cv

核心模塊&#xff1a; rasterizer&#xff1a;光柵化器&#xff0c;負責三角形遍歷和像素繪制Shader&#xff1a;包含頂點著色器和多種片元著色器Texture&#xff1a;紋理處理模塊 頂點著色器的計算量一般遠小于片元著色器。因為組成三角形的頂點相對有限&#xff0c;而片元需…

mac使用Homebrew安裝miniconda(mac搭建python環境),并在IDEA中集成miniconda環境

一、安裝Homebrew mac安裝brew 二、使用Homebrew安裝miniconda brew search condabrew install miniconda安裝完成后的截圖&#xff1a; # 查看是否安裝成功 brew list環境變量&#xff08;無需手動配置&#xff09; 先執行命令看能不能正常返回&#xff0c;如果不能正常…

多視圖幾何--相機標定--從0-1理解張正友標定法

1基本原理 1.1 單應性矩陣&#xff08;Homography&#xff09;的建立 相機模型&#xff1a;世界坐標系下棋盤格平面&#xff08;Z0&#xff09;到圖像平面的投影關系為&#xff1a; s [ u v 1 ] K [ r 1 r 2 t ] [ X Y 1 ] s \begin{bmatrix} u \\ v \\ 1 \end{bmatrix} K…

WWDG窗口看門狗原理

WWDG&#xff08;窗口看門狗&#xff09;在窗口期喂狗 作用&#xff1a; 原理&#xff1a; 框圖 WWDG寄存器&#xff1a; WWDG_CR控制寄存器 WWDG_CFR配置寄存器 狀態寄存器WWDG_SR 超時時間計算公式 最小最大超時值 HAL配置函數&#xff1a; 1. IWDG 和 WWDG 的區別 IWDG&…

無公網IP也能遠程控制Windows:Linux rdesktop內網穿透實戰

文章目錄 前言1. Windows 開啟遠程桌面2. Linux安裝rdesktop工具3. Win安裝Cpolar工具4. 配置遠程桌面地址5. 遠程桌面連接測試6. 設置固定遠程地址7. 固定地址連接測試 前言 如今遠程辦公已經從一種選擇變成了許多企業和個人的必修課&#xff0c;而如何在Linux系統上高效地訪…

Pygame實現射擊鴨子游戲3-2

2 鴨子類Target的創建 2.1 __init__()函數 Target類的__init__()函數代碼如圖5所示。 圖5 __init__()函數代碼 其中&#xff0c;第18行將Target類聲明為pygame.sprite.Sprite類的子類&#xff1b;第19行代碼中&#xff0c;__init__()函數的img_path參數表示鴨子圖片的文件名…

利用Java爬蟲獲取衣聯網商品詳情:實戰指南

在電商領域&#xff0c;獲取商品詳情是數據分析和市場研究的重要環節。衣聯網作為知名的電商平臺&#xff0c;提供了豐富的服裝商品資源。本文將詳細介紹如何利用Java編寫爬蟲程序&#xff0c;通過商品ID獲取衣聯網商品詳情。 一、準備工作 &#xff08;一&#xff09;環境搭…

五、OpenGL中Shader與C++數據傳輸

文章目錄 一、概述二、Shader 代碼文件的基本格式三、Shader的向量語法介紹四、Shader之間的數據傳輸五、Shader與C的數據傳輸uniform六、完整示例 一、概述 在 OpenGL 中&#xff0c;Shader&#xff08;著色器&#xff09;使用 GLSL&#xff08;OpenGL Shading Language&…

【3DMAX插件】3DMAX建筑大師插件MasterBuilder使用方法

3DMAX建筑大師插件是一款專為3DMAX設計的程序化&#xff08;參數化&#xff09;建筑建模工具&#xff0c;其最大特點是能夠一鍵生成建筑模型&#xff0c;極大地提升了工作效率。該插件配備了多種結構控制選項&#xff0c;涵蓋陽臺、門窗、欄桿、樓頂水塔等附屬建筑元素&#xf…