【C】 24_#pragma 使用分析

pragma 簡介

  • #pragma 用於指示編譯器完成一些特定的動做
  • #pragma 所定義的不少指示字是編譯器特有
  • #pragma 在不一樣的編譯器間是不可移植編程

    • 預處理器將忽略它不認識的#pragma指令
    • 不一樣的編譯器可能以不一樣的方式解析同一條#pragma指令

通常用法:ide

'#pragma parameter'

注: 不一樣的 parameter 參數語法和意義各不相同

C 語言預留給編譯器廠商的擴展指示字性能

pragma message

  • message 參數在大多數的編譯器中都有類似的實現
  • message 參數在編譯時輸出消息到編譯輸出窗口中
  • message 用於條件編譯中可提示代碼的版本信息
#if defined(ANDROID20)
    #pragma message("Compile Android SDK 2.0 ...")
    #define VERSION "Android 2.0"
#endif
與 #error 和 #warning 不一樣,#pragma message 僅僅表明一條編譯消息,不表明程序錯誤。

實例分析: #pragma message 使用示例

#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!    
#endif

int main()
{
    printf("%s\n", VERSION);

    return 0;
}
編譯輸出:
[GCC]  test.c:10: note: #pragma message: Compile Android SDK 4.0...
[VC]   Compile Android SDK 4.0...

運行輸出:
[GCC]  Android 4.0
[VC]   Android 4.0

pragma once

  • #pragma once 用於保證頭文件只被編譯一次
  • #pragma once 是編譯器相關的,不必定被支持
#ifndef _HEADER_H_
#define _HEADER_H_

// source code

#endif

spa

#pragma oncecode

這兩種方式有什麼區別呢?ip

  • #ifndef 爲 C 語言支持,實質包含了屢次,經過宏來決定是否選擇是否嵌入到源代碼中,預處理器處理屢次,保證只「嵌入一次」。
  • #pragma once 爲編譯器支持,是真正的只編譯一次,以後遇到此文件,直接不作處理。
  • #pragma once 具備更高的編譯效率。

示例分析: #pragma once 使用分析

Test.c內存

#include <stdio.h>
#include "global.h"
#include "global.h"

int main()
{
    printf("g_value = %d\n", g_value);

    return 0;
}

global.h編譯器

#pragma once

int g_value = 1;
輸出:
g_value = 1

VC GCC  : 無警告,無錯誤              【主持】
BCC     : 編譯出錯, g_value屢次定義  【不支持】
  • 兼顧移植性與編譯效率的方法
#ifndef _HEADER_H_
    #define _HEADER_H_
    
    #pragma once
    
    // source code
    
    #endif

pragma pack

  • 什麼是內存對齊it

    • 不一樣類型的數據在內存中按照必定的規則排列
    • 而不必定是順序的一個接一個的排列
struct Test1
{
    char c1;
    short s2;
    char c2;
    int i;
};

struct Test2
{
    char c1;
    char c2;
    short s2;
    int i;
};

Test1 和 Test2 所佔的內存空間是否相同?io

sizeof(struct Test1) = 12
sizeof(struct Test2) = 8

clipboard.png

爲何須要內存對齊?

  • CPU 對內存的讀取不是連續的,而是分紅塊讀取的,塊的大小隻能是一、二、四、八、16...字節

    • 當讀取操做的數據未對齊,則須要兩次總線來訪問內存,所以性能會大打折扣
    • 某些硬件平臺只能從規定的相對地址讀取特定類型的數據,不然產生硬件異常
  • #pragm pack 用於指定內存對齊方式

未對齊形成兩次內存讀取【32位機器的讀寫最小粒度4字節】
clipboard.png

#pragma pack 可以改變編譯器的默認對齊方式

#pragma pack(1)
struct Test1
{
    char c1;
    short s2;
    char c2;
    int i;
};
#pragma pack()

#pragma pack(1)
struct Test2
{
    char c1;
    char c2;
    short s2;
    int i;
};
#pragma pack()

sizeof(struct Test1) = 8
sizeof(struct Test2) = 8

struct 佔用的內存大小

  • 第一個成員起始於 0 偏移處
  • 每一個成員按其類型大小和pack參數中較小的一個進行對齊

    • 偏移地址必須能被對齊參數整除
    • 結構體成員的對齊參數大小取其內部pack參數與內部長度最大的數據成員之間較小的做爲其對齊參數大小
  • 結構體總長度必須爲全部對齊參數的整數倍

編譯器默認狀況下按照 4 字節對齊

編程實驗: 結構體大小計算

Test_1.c

#include <stdio.h>

#pragma pack(2)
struct Test1
{               // 對齊參數  偏移地址  大小
    char c1;    // 1        0       1
    short s2;   // 2        2       2
    char c2;    // 1        5       2     
    int i;      // 2        6       4
};
#pragma pack()

#pragma pack(4)
struct Test2
{               // 對齊參數  偏移地址  大小
    char c1;    // 1        0       1
    char c2;    // 1        1       1
    short s2;   // 2        2       2
    int i;      // 4        4       4
};
#pragma pack()

int main()
{
    printf("%d\n", sizeof(struct Test1));    
    printf("%d\n", sizeof(struct Test2));    
}
輸出:
10
8

Test_2.c

#include <stdio.h>

#pragma pack(8)

struct S1    
{                 // 對齊參數 偏移地址  大小
    short s;      // 2       0       1
    long b;       // 4       4       4
};                // 總體長度爲全部對齊參數的整數倍, len = 4 + 4 = 8

struct S2
{                 // 對齊參數  偏移地址 大小
    char c;       // 1        0      1
    struct S1 d;  // 4        4      8
    double e;     // 8        16     8
};                // 總體長度爲全部對齊參數的整數倍 , len = 8 + 16 = 24

#pragma pack()

int main()
{
    printf("%d\n", sizeof(struct S1));
    printf("%d\n", sizeof(struct S2));
}
輸出:[GCC] 
8
20   【截至2018/12/04,GCC 暫不支持8字節對齊,忽略pack(8),默認4字節對齊】
 
輸出:[VC]
8
20

小結

  • #pragma 用於指示編譯器完成一些特定的動做
  • #pragma 所定義的不少指示字都是編譯器特有的

    • #pragma message 用於自定義編譯消息
    • #pragma once 用於保證頭文件只被編譯一次
    • #pragma pack 用於指定內存對齊方式

以上內容參考狄泰軟件學院系列課程,請你們保護原創!

相關文章
相關標籤/搜索