日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 人工智能 > pytorch >内容正文

pytorch

【C语言进阶深度学习记录】十九 #pragma使用与分析

發(fā)布時(shí)間:2023/12/10 pytorch 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【C语言进阶深度学习记录】十九 #pragma使用与分析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

    • 1 #pragma 概念簡(jiǎn)介
      • 1.1 #pragma message 的用法
      • 1.2 #pragma once 的用法
      • 1.3 #pragma pack 的用法
        • 1.31 struct占用的內(nèi)存大小如何計(jì)算
    • 2 總結(jié)

在學(xué)習(xí) #pragma 之前 ,我們首先要明白一點(diǎn), #pragma 的實(shí)現(xiàn),在不同的編譯器之間是不同的,所以使用它的代碼,基本上不能移植代碼。但是它也有它自己的用處,還是要學(xué)習(xí)以下。

1 #pragma 概念簡(jiǎn)介

  • #pragma 是唯一一個(gè)預(yù)處理器不處理的指令,它需要保留給編譯器處理。
  • #pragma 用于指示編譯器完成一些特殊的動(dòng)作 (看后面就知道什么意思了)
  • #pragma 所定義的很多指示字,是編譯器特有的
  • #pragma 在不同的編譯器之間是不能移植的

因?yàn)椴煌木幾g器的 #pragma 的實(shí)現(xiàn)是不同的。所以:

  • 編譯器將忽略它不認(rèn)識(shí)的 #pragma 指令。
  • 不同的編譯器可能以不同的方式來解釋同一條 #pragma 指令
  • 一般用法為:

    注意:不同的 parameter 參數(shù)的語(yǔ)法和意義各不相同

    1.1 #pragma message 的用法

    • message的參數(shù)在大多數(shù)的編譯器中都有相似的實(shí)現(xiàn)
    • message參數(shù)在編譯時(shí),輸出消息到編譯輸出窗口中。注意是在編譯時(shí),不是程序運(yùn)行時(shí)。
    • 如果將message用于條件編譯中,可以提示代碼的版本信息。如下圖所示:

    如下代碼是 #pragma message的使用分析

    • 24-1-lyy.c
    #include <stdio.h>#if defined(ANDROID20)#pragma message("Compile Android SDK 2.0...")#define VERSION "Android 2.0" #elif defined(ANDROID23)#pragma message("Compile Android SDK 2.3...")#define VERSION "Android 2.3" #elif defined(ANDROID40)#pragma message("Compile Android SDK 4.0...")#define VERSION "Android 4.0" #else #error Compile Version is not Provided! #endifint main(){printf("%s\n", VERSION);return 0; }

    上面的 #error 的意思是如果沒有定義上述的宏,就會(huì)將這句話在編譯的時(shí)候打印出來,代表我們想要了解的錯(cuò)誤。

    • 上述代碼如果這樣編譯,不定義宏:gcc 24-1-lyy.c -o 24-1-lyy.out 在編譯時(shí)將顯示錯(cuò)誤如下:

    • 如果這樣編譯,在命令行中定義宏 ANDROID23 ,gcc -DANDROID23 24-1-lyy.c -o 24-1-lyy.out 將在編譯時(shí)顯示如下信息:

    注意一點(diǎn),上述的信息是在編譯的時(shí)候打印輸出的,不是在程序運(yùn)行的時(shí)候輸出的。

    1.2 #pragma once 的用法

    首先說一下它的作用:

    • #pragma once 用于保證頭文件只被編譯一次。也就是可以避免重復(fù)包含頭文件。這與上一篇文章使用條件編譯避免重復(fù)包含頭文件的作用是一樣的:【C語(yǔ)言進(jìn)階深度學(xué)習(xí)記錄】十八 條件編譯的使用與分析
    • #pragma once 是編譯器相關(guān)的,不一定被支持。

    那么 #pragma once 與之前學(xué)的條件編譯來避免重復(fù)包含頭文件,這兩種方式有什么區(qū)別呢?

    • #pragma once效率會(huì)更高,因?yàn)樗槐WC被編譯一次,不會(huì)去判斷是否定定義了相關(guān)宏。所以效率更高。

    下面的代碼:

    • 24-2.c
    #include <stdio.h> #include "global.h" #include "global.h"int main() {printf("g_value = %d\n", g_value);return 0; }
    • global.h
    #pragma onceint g_value = 1;
    • 對(duì)上述代碼進(jìn)行編譯運(yùn)行:gcc 24-2.c -o 24-2.out

    可以看出,雖然上面的代碼包含了兩次global頭文件,但是編譯并沒有報(bào)錯(cuò)。因?yàn)樵陬^文件中使用了#pragma once ,使得該頭文件只能被編譯一次,作用與在頭文件中使用條件編譯指令一樣。

    1.3 #pragma pack 的用法

    什么是內(nèi)存對(duì)齊?

    • 不同類型的數(shù)據(jù)在內(nèi)存中按照一定的規(guī)則排列
    • 但是不一定是順序的一個(gè)接一個(gè)的排列

    例如下圖中的兩個(gè)結(jié)構(gòu)體的大小是不一樣的,因?yàn)樗鼈兊膬?nèi)存布局是不一樣的:

    它們的內(nèi)存布局如下圖:

    至于為什么是上圖這樣的對(duì)齊方式,下一篇文章會(huì)進(jìn)行學(xué)習(xí)。

    先來加單的說一下為什么需要內(nèi)存對(duì)齊:

  • CPU對(duì)內(nèi)存的讀取不是連續(xù)的,而是分成塊讀取的,塊的大小只能是1,2,4,8,16…字節(jié)
  • 當(dāng)讀取的數(shù)據(jù)未對(duì)齊,則需要兩次總線周期來訪問內(nèi)存才能將整個(gè)數(shù)據(jù)讀完,這樣會(huì)降低CPU性能
  • 某些硬件平臺(tái)只能從規(guī)定的相對(duì)地址處讀取特定類型的數(shù)據(jù),否則產(chǎn)生硬件異常
    • #pragma pack 用于指定內(nèi)存的對(duì)齊方式。用于修改編譯器的默認(rèn)對(duì)齊方式

    一般來講,在Linux系統(tǒng)中,編譯器的默認(rèn)對(duì)我方式是4字節(jié)對(duì)齊。下圖中的代碼,可以將對(duì)齊方式修改為1字節(jié)對(duì)齊:

    1.31 struct占用的內(nèi)存大小如何計(jì)算

    對(duì)于不同的內(nèi)存對(duì)齊方式,上面的結(jié)構(gòu)體在內(nèi)存中的布局是不一樣的,那么我們?nèi)绾蝸碛?jì)算不同的對(duì)齊方式在內(nèi)存中的布局是什么樣的呢?

    需要根據(jù)以下三點(diǎn):

  • 第一個(gè)struct成員永遠(yuǎn)起始于 0偏移處

  • 每個(gè)成員按其類型大小和pack參數(shù)中較小的一個(gè) 進(jìn)行對(duì)齊

    2.1 偏移地址必須能被對(duì)齊參數(shù)整除
    2.2 如果一個(gè)結(jié)構(gòu)體中有一個(gè)變量也是結(jié)構(gòu)體,那么這個(gè)內(nèi)部的結(jié)構(gòu)體成員的對(duì)齊大小就按照其內(nèi)部最大的數(shù)據(jù)成員作為其大小來計(jì)算

  • 結(jié)構(gòu)體總長(zhǎng)度,必須為所有對(duì)齊參數(shù)的整數(shù)倍

    • 只需要按照上述三點(diǎn)計(jì)算方法,就可以計(jì)算出所有結(jié)構(gòu)體的大小以及內(nèi)存布局的樣式

    如下面的代碼:

    • 24-3.c
    #include <stdio.h>#pragma pack(2) struct Test1 {char c1;short s;char c2;int i; }; #pragma pack()#pragma pack(4) struct Test2 {char c1;char c2;short s;int i; }; #pragma pack()int main() {printf("sizeof(Test1) = %d\n", sizeof(struct Test1));printf("sizeof(Test2) = %d\n", sizeof(struct Test2));return 0; }
    • 編譯運(yùn)行結(jié)果為:

    sizeof(Test1) = 10
    sizeof(Test2) = 8

    • 結(jié)果分析1–Test1:

    對(duì)于結(jié)構(gòu)體Test1,2字節(jié)對(duì)齊,按照上述三個(gè)計(jì)算條件有:

    struct Test1 { //對(duì)齊方式 大小 起始地址 占用的內(nèi)存地址位置 char c1; 2 大于 1 0 0 short s; 2 等于 2 2(被對(duì)齊參數(shù)2整除) 2~3 char c2; 2 大于 1(對(duì)齊參數(shù)) 4 4 int i; 2(對(duì)齊參數(shù)) 小于 4 6(被對(duì)齊參數(shù)2整除) 6~9 };
  • Test1的對(duì)齊方式是2字節(jié)。
  • 然后根據(jù):

  • 每個(gè)成員按其類型大小和pack參數(shù)中較小的一個(gè) 進(jìn)行對(duì)齊。比如上面的起始地址計(jì)算那里,都是被對(duì)齊參數(shù)2整除,這個(gè)2是類型大小和pack參數(shù)中較小的一個(gè)

    2.1 偏移地址必須能被對(duì)齊參數(shù)整除

  • 這一條規(guī)則計(jì)算每個(gè)成員的起始地址。如上面的計(jì)算。

  • 最后再看總體大小是否是對(duì)齊參數(shù)的整數(shù)倍。上面Test1 大小是10,是對(duì)齊參數(shù)2的整數(shù)倍
    • 結(jié)果分析2–Test2

    對(duì)于結(jié)構(gòu)體Test2,4字節(jié)對(duì)齊,按照上述三個(gè)計(jì)算條件有:

    struct Test2 { //對(duì)齊方式 大小 起始地址 占用的內(nèi)存地址位置 char c1; 4 大于 1(對(duì)齊參數(shù)) 0 0 char c2; 4 大于 1(對(duì)齊參數(shù)) 1(被對(duì)齊參數(shù)1整除) 1 short s; 4 大于 2(對(duì)齊參數(shù)) 2(被對(duì)齊參數(shù)2整除) 2~3 int i; 4 等于 4(對(duì)齊參數(shù)) 4(被對(duì)齊參數(shù)4整除) 5~7 };
  • Test2 的對(duì)齊方式是4字節(jié)對(duì)齊
  • 然后再根據(jù):

  • 每個(gè)成員按其類型大小和pack參數(shù)中較小的一個(gè) 進(jìn)行對(duì)齊。比如上面的起始地址計(jì)算那里,對(duì)齊參數(shù)分別為1,1,2,4

    2.1 偏移地址必須能被對(duì)齊參數(shù)整除

  • 這一條規(guī)則計(jì)算每個(gè)成員的起始地址。如上面的計(jì)算。

  • 最后再看總體大小是否是對(duì)齊參數(shù)的整數(shù)倍。上面Test2 大小是8,是對(duì)齊參數(shù)4的整數(shù)倍
  • 經(jīng)過上面的兩個(gè)結(jié)構(gòu)體大小的計(jì)算,可以很容易的畫出其內(nèi)存圖

    再看一個(gè)復(fù)雜的結(jié)構(gòu)體大小的計(jì)算

    • 代碼 24-4.c
    #include <stdio.h>struct S1 {short a;long b; };struct S2 {char c;struct S1 d;double e; };int main() {printf("sizeof(struct S1) = %d\n", sizeof(struct S1));printf("sizeof(struct S2) = %d\n", sizeof(struct S2));return 0; }

    運(yùn)行結(jié)果為:

    sizeof(struct S1) = 8
    sizeof(struct S2) = 20

    S1很好分析。下面我們分析S2

    struct S2 //對(duì)齊方式 大小 起始地址 占用的內(nèi)存地址位置 {char c; 4 大于 1(對(duì)齊參數(shù)) 0 0 struct S1 d; 4 等于 4(這個(gè)是S1中最大參數(shù)大小) 4(4整除) 4~11(S1實(shí)際大小為8double e; 4(對(duì)齊)小于 8 12(4整除) 12~19 };
  • S2 的對(duì)齊方式是4字節(jié)對(duì)齊
  • 然后再根據(jù):

  • 每個(gè)成員按其類型大小和pack參數(shù)中較小的一個(gè) 進(jìn)行對(duì)齊。比如上面的起始地址計(jì)算那里,對(duì)齊參數(shù)分別為0,4,12

    2.1 偏移地址必須能被對(duì)齊參數(shù)整除
    2.2 如果一個(gè)結(jié)構(gòu)體中有一個(gè)變量也是結(jié)構(gòu)體,那么這個(gè)內(nèi)部的結(jié)構(gòu)體成員的對(duì)齊大小就按照其內(nèi)部最大的數(shù)據(jù)成員作為其大小來計(jì)算

  • 這一條規(guī)則計(jì)算每個(gè)成員的起始地址。如上面的計(jì)算。

  • 最后再看總體大小是否是對(duì)齊參數(shù)的整數(shù)倍。上面S2 大小是20,是對(duì)齊參數(shù)4的整數(shù)倍
  • 我所使用的編譯器gcc 4.4.5 不支持8字節(jié)對(duì)齊方式

    2 總結(jié)

    • #pragma 用于指示編譯器完成以下特殊的動(dòng)作
    • #pragma 對(duì)于不同的編譯器,可能底層實(shí)現(xiàn)原理不太一樣
    • #pragma message 用于自定義編譯消息
    • #pragma once 用于避免重復(fù)包含頭文件
    • #pragma pack 用于指定內(nèi)存對(duì)齊方式

    總結(jié)

    以上是生活随笔為你收集整理的【C语言进阶深度学习记录】十九 #pragma使用与分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。