C語言再學習15——預處理(三)

前言:

爲了方便查看博客,特意申請了一個公衆號,附上二維碼,有興趣的朋友可以關注,和我一起討論學習,一起享受技術,一起成長。

在這裏插入圖片描述


1. #pragma pack

測試:

#include <stdio.h>

int main(void)
{
	struct tagTestStruct_T
	{
		char ch;	//1字節
		short sh;	//2字節
		char str;	//1字節
		int i;		//4字節
	};

	struct tagTestStruct_T st_TestStruct;

	printf("ch %p,sh %p,str %p,i %p \r\n",
		(unsigned int)(void *)&st_TestStruct.ch - (unsigned int)(void *)&st_TestStruct,
		(unsigned int)(void *)&st_TestStruct.sh - (unsigned int)(void *)&st_TestStruct, 
		(unsigned int)(void *)&st_TestStruct.str - (unsigned int)(void *)&st_TestStruct,
		(unsigned int)(void *)&st_TestStruct.i - (unsigned int)(void *)&st_TestStruct);

	system("pause");
	return 0;
}

輸出:
在這裏插入圖片描述

2. 內存對齊

字(2字節),雙字(4字節),和四字(8字節)在自然邊界上不需要在內存中對齊。(對字,雙字,和四字來說,自然邊界分別是偶數地址,可以被 4 整除的地址,和可以被 8 整除的地址。)無論如何,爲了提高程序的性能,數據結構(尤其是棧)應該儘可能地在自然邊界上對齊。原因在於:爲了訪問未對齊的內存,處理器需要作兩次內存訪問;然而,對齊的內存訪問僅需要一次訪問。

了提高 CPU 的存儲速度,編譯器對一些變量的起始地址做了“對齊”處理。在默認情況下,編譯器規定各成員變量存放的起始地址相對於結構的起始地址的偏移量必須爲該變量的類型所佔用的字節數的倍數。下面列出常用類型的對齊方式:

類型 對齊方式
char 偏移量必須爲sizeof(char)即1的倍數
int 偏移量必須爲sizeof(int)即4的倍數
float 偏移量必須爲sizeof(float)即4的倍數
double 偏移量必須爲sizeof(double)即8的倍數
short 偏移量必須爲sizeof(short)即2的倍數

各成員變量在存放的時候根據在結構中出現的順序依次申請空間,同時按照上面的對齊方式調整位置,空缺的字節編譯器會自動填充。同時編譯器爲了確保結構的大小爲結構的字節邊界數(即該結構中佔用最大空間的類型所佔用的字節數)的倍數,所以在爲最後一個成員變量申請空間後,還會根據需要自動填充空缺的字節。

注:

對齊方式:變量存放的起始地址相對於結構的起始地址的偏移量;

3. 對齊規則

每個平臺都有自己默認的“對齊係數”,在 VS2013 上測試爲 4 字節對齊。我們可以通過命令 #pragma pack(n) 對內存進行對齊。通過預編譯命令#pragma pack(n),n = 1,2,4,8,16 來改變對齊係數。

有效對齊值:是 #pragma pack(n) 指定的長度 n 與結構體中的成員變量中比較小的。

eg:
#pragma pack(1) 指定一字節對齊,而結構體中也定義有一字節的變量,那麼對齊單位就是一字節;同樣地,如果 #pragma pack(2) 指定 2 字節對齊,而結構體中也定義沒有一字節的變量,那麼對齊單位就是 2 字節,若結構體中有一字節變量,還是以一字節對齊;

如下的測試:

#include <stdio.h>

#pragma pack(2)

int main(void)
{
	struct tagTestStruct_T
	{
		short sh;
		int i;
	};

	struct tagTestStruct_T st_TestStruct;

	printf("size is %d \r\n",sizeof(st_TestStruct));

	system("pause");
	return 0;
}

輸出結果爲 2 字節對齊:

在這裏插入圖片描述注:

使用指令 #pragma pack (n),設置編譯器將按照 n 個字節對齊。
使用指令 #pragma pack (),編譯器將取消自定義字節對齊方式。

4. 規則測試

定義如下結構體:

#include <stdio.h>

//#pragma pack(1)

int main(void)
{
	struct tagTestStruct_T
	{
		char ch;
		short sh;
		char str;
		int i;
	};

	struct tagTestStruct_T st_TestStruct;

	printf("size is %d \r\n",sizeof(st_TestStruct));

	system("pause");
	return 0;
}

4.1 採用默認對齊

測試程序先註釋掉://#pragma pack(1)。輸出結果爲:
在這裏插入圖片描述內存邊界從 4 的倍數開始,如下圖:

在這裏插入圖片描述

4.2 設置 1 字節對齊

測試程序先註釋掉:打開 #pragma pack(1)。輸出結果爲:

在這裏插入圖片描述
內存邊界從 1 開始,如下圖:

在這裏插入圖片描述


參考:

1.C/C++內存對齊詳解
2.C語言內存對齊詳解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章