C++11中alignof和alignas的入門(mén)
一、引言
在C++編程中,內(nèi)存對(duì)齊是一個(gè)重要的概念,它關(guān)乎于數(shù)據(jù)在內(nèi)存中如何布局以提高訪問(wèn)效率。C++11標(biāo)準(zhǔn)引入了兩個(gè)關(guān)鍵的特性來(lái)支持內(nèi)存對(duì)齊:alignof
和alignas
。這兩個(gè)特性提供了對(duì)內(nèi)存對(duì)齊的直接控制,讓開(kāi)發(fā)者能夠更好地優(yōu)化程序性能。本文將深入介紹alignof
和alignas
的相關(guān)知識(shí),幫助小白從入門(mén)到精通。
二、內(nèi)存對(duì)齊的概念和作用
2.1 什么是內(nèi)存對(duì)齊
內(nèi)存對(duì)齊是指數(shù)據(jù)在內(nèi)存中的存儲(chǔ)地址必須滿(mǎn)足特定的對(duì)齊要求,通常是該類(lèi)型大小的倍數(shù)。例如,int
類(lèi)型通常對(duì)齊到4字節(jié)邊界,double
類(lèi)型通常對(duì)齊到8字節(jié)邊界。內(nèi)存對(duì)齊是一個(gè)整數(shù),意味著該數(shù)據(jù)成員地址只能位于內(nèi)存對(duì)齊的倍數(shù)上,而對(duì)齊之間的未使用空間被稱(chēng)為填充數(shù)據(jù)。
以下代碼展示了內(nèi)存對(duì)齊的現(xiàn)象:
#include <iostream> using namespace std; struct HowManyBytes{ char a; int b; }; int main() { cout << "sizeof(char): " << sizeof(char) << endl; cout << "sizeof(int): " << sizeof(int) << endl; cout << "sizeof(HowManyBytes): " << sizeof(HowManyBytes) << endl; cout << endl; cout << "offset of char a: " << offsetof(HowManyBytes, a) << endl; //0 cout << "offset of int b: " << offsetof(HowManyBytes, b) << endl; //4 return 0; }
在上述代碼中,成員a
占1個(gè)字節(jié),成員b
占4字節(jié),但結(jié)構(gòu)體HowManyBytes
的大小為8字節(jié),這是因?yàn)镃/C++對(duì)數(shù)據(jù)結(jié)構(gòu)有著對(duì)齊要求,b
的位置為4而不是1,a
之后的1、2、3三個(gè)字節(jié)為填充數(shù)據(jù)。
2.2 內(nèi)存對(duì)齊的優(yōu)勢(shì)
內(nèi)存對(duì)齊主要有以下兩點(diǎn)優(yōu)勢(shì):
- 跨平臺(tái):有些平臺(tái)要求內(nèi)存對(duì)齊,否則程序無(wú)法運(yùn)行。不同硬件平臺(tái)對(duì)存儲(chǔ)空間的處理上存在很大的不同,某些平臺(tái)對(duì)特定類(lèi)型的數(shù)據(jù)只能從特定地址開(kāi)始存取,而不允許其在內(nèi)存中任意存放。例如Motorola 68000處理器不允許16位的字存放在奇地址,否則會(huì)觸發(fā)異常,因此在這種架構(gòu)下編程必須保證字節(jié)對(duì)齊。
- 性能:內(nèi)存對(duì)齊有利于提高數(shù)據(jù)緩存速度。盡管內(nèi)存是以字節(jié)為單位,但是大部分處理器并不是按字節(jié)塊來(lái)存取內(nèi)存的,它一般會(huì)以雙字節(jié)、四字節(jié)、8字節(jié)、16字節(jié)甚至32字節(jié)為單位來(lái)存取內(nèi)存,我們將上述這些存取單位稱(chēng)為內(nèi)存存取粒度。假如沒(méi)有內(nèi)存對(duì)齊機(jī)制,數(shù)據(jù)可以任意存放,處理器在讀取數(shù)據(jù)時(shí)可能需要進(jìn)行多次內(nèi)存訪問(wèn)才能獲取完整的數(shù)據(jù),這會(huì)顯著降低性能。而合理的內(nèi)存對(duì)齊可以減少CPU的內(nèi)存訪問(wèn)開(kāi)銷(xiāo),提高程序的運(yùn)行效率。
三、alignof運(yùn)算符
3.1 定義和作用
alignof
是一個(gè)操作符,用于查詢(xún)類(lèi)型或變量的對(duì)齊要求。它返回一個(gè)std::size_t
類(lèi)型的值,表示類(lèi)型或變量的對(duì)齊字節(jié)數(shù)。在C++11之前,對(duì)齊方式是無(wú)法得知的,只能自己判斷,且不同的平臺(tái)實(shí)現(xiàn)方式可能不同,而alignof
操作符可以讓開(kāi)發(fā)者在編譯期確定某個(gè)數(shù)據(jù)類(lèi)型的內(nèi)存對(duì)齊要求。
3.2 語(yǔ)法規(guī)則
alignof
的語(yǔ)法非常簡(jiǎn)單,其基本形式為:
alignof(type);
其中type
是要查詢(xún)對(duì)齊要求的類(lèi)型,可以是基本類(lèi)型、結(jié)構(gòu)體、類(lèi)等。例如:
#include <iostream> struct MyStruct { char c; int i; }; int main() { std::cout << "Alignment of char: " << alignof(char) << std::endl; std::cout << "Alignment of int: " << alignof(int) << std::endl; std::cout << "Alignment of MyStruct: " << alignof(MyStruct) << std::endl; return 0; }
在上述代碼中,alignof(char)
返回char
類(lèi)型的對(duì)齊字節(jié)數(shù),通常為1;alignof(int)
返回int
類(lèi)型的對(duì)齊字節(jié)數(shù),通常為4;alignof(MyStruct)
返回結(jié)構(gòu)體MyStruct
的對(duì)齊字節(jié)數(shù),取決于結(jié)構(gòu)體中最大對(duì)齊要求的成員,這里為4。
3.3 使用示例
下面是更多關(guān)于alignof
的使用示例:
#include <iostream> struct Foo { int i; float f; char c; }; struct Empty {}; struct alignas(64) Empty64 {}; int main() { std::cout << "Alignment of" "\n" "- char : " << alignof(char) << "\n" "- pointer : " << alignof(int*) << "\n" "- class Foo : " << alignof(Foo) << "\n" "- empty class : " << alignof(Empty) << "\n" "- alignas(64) Empty: " << alignof(Empty64) << "\n"; return 0; }
運(yùn)行上述代碼,輸出結(jié)果如下:
Alignment of
- char : 1
- pointer : 8
- class Foo : 4
- empty class : 1
- alignas(64) Empty: 64
從輸出結(jié)果可以看出,alignof
可以準(zhǔn)確地查詢(xún)出不同類(lèi)型的對(duì)齊要求。
3.4 注意事項(xiàng)
- 不支持獲取不完整類(lèi)型或變量對(duì)齊值:C++11支持操作符
alignof
獲取定義完整類(lèi)型的內(nèi)存對(duì)齊要求,但不支持獲取不完整類(lèi)型或變量對(duì)齊值。例如:
#include <iostream> using namespace std; class InComplete; struct Completed{}; int main() { int a; long long b; auto & c = b; char d[1024]; // 對(duì)內(nèi)置類(lèi)型和完整類(lèi)型使用alignof cout << alignof(int) << endl; // 4 cout << alignof(Completed) << endl; // 1 // 對(duì)變量、引用或者數(shù)組使用alignof,以下代碼無(wú)法編譯 // cout << alignof(a) << endl; // cout << alignof(b) << endl; // cout << alignof(c) << endl; // cout << alignof(d) << endl; // 本句無(wú)法通過(guò)編譯,Incomplete類(lèi)型不完整 // cout << alignof(InComplete) << endl; return 0; }
- 跨平臺(tái)差異:不同編譯器和不同平臺(tái)對(duì)基本類(lèi)型的默認(rèn)對(duì)齊要求可能略有不同,因此使用
alignof
時(shí)需要注意平臺(tái)兼容性問(wèn)題。
四、alignas說(shuō)明符
4.1 定義和作用
alignas
是一個(gè)對(duì)齊說(shuō)明符,用于指定變量或類(lèi)型的最小對(duì)齊要求。alignas
可以用于變量聲明或類(lèi)型定義中,以確保所聲明的變量或類(lèi)型實(shí)例具有特定的對(duì)齊。它允許開(kāi)發(fā)者顯式指定類(lèi)型或?qū)ο蟮膶?duì)齊方式,而不是依賴(lài)于編譯器的默認(rèn)對(duì)齊方式。
4.2 語(yǔ)法規(guī)則
alignas
的語(yǔ)法如下:
alignas(alignment) type variable;
其中alignment
是一個(gè)整數(shù)或常量表達(dá)式,表示字節(jié)對(duì)齊數(shù),type
是聲明的類(lèi)型,variable
是變量。alignment
必須是求值為零或合法的對(duì)齊或擴(kuò)展對(duì)齊的整型常量表達(dá)式,且通常為2的冪次方(如1、2、4、8、16等)。例如:
struct alignas(16) MyStruct { int x; float y; };
在上述代碼中,MyStruct
被指定為16字節(jié)對(duì)齊,即每個(gè)MyStruct
類(lèi)型的對(duì)象都必須在內(nèi)存中以16字節(jié)對(duì)齊的方式存儲(chǔ)。
4.3 使用示例
下面是一些關(guān)于alignas
的使用示例:
#include <iostream> // 每個(gè) sse_t 類(lèi)型的對(duì)象將會(huì)按照 32 字節(jié)的邊界對(duì)齊: struct alignas(32) sse_t { float sse_data[4]; }; // 數(shù)組 cacheline 將會(huì)按照 64 字節(jié)的邊界對(duì)齊: using cacheline_t = alignas(64) char[64]; cacheline_t cacheline; int main() { sse_t x; std::cout << "Alignment of sse_t: " << alignof(sse_t) << std::endl; std::cout << "Address of x: " << &x << std::endl; std::cout << "Alignment of cacheline_t: " << alignof(cacheline_t) << std::endl; std::cout << "Address of cacheline: " << &cacheline << std::endl; return 0; }
運(yùn)行上述代碼,輸出結(jié)果如下:
Alignment of sse_t: 32
Address of x: 0x7ffef1f24c40
Alignment of cacheline_t: 64
Address of cacheline: 0x7ffef1f24c80
從輸出結(jié)果可以看出,x
的地址是以32字節(jié)對(duì)齊的,cacheline
的地址是以64字節(jié)對(duì)齊的,說(shuō)明alignas
成功地指定了類(lèi)型的對(duì)齊要求。
4.4 注意事項(xiàng)
- 表達(dá)式要求:對(duì)于
alignas(expression)
,表達(dá)式必須是0或冪為2(1、2、4、8、16、…)的整型常量表達(dá)式。所有其他表達(dá)式的格式不正確,要么會(huì)被編譯器忽略掉。 - 不能修飾的對(duì)象:
alignas
不能應(yīng)用于函數(shù)形參或catch
子句的異常形參。例如:
alignas(double) void f(); // 錯(cuò)誤:alignas不能修飾函數(shù)
- 對(duì)齊要求不能削弱自然對(duì)齊:如果某個(gè)聲明上的最嚴(yán)格(最大)
alignas
比當(dāng)它沒(méi)有任何alignas
說(shuō)明符的情況下本應(yīng)有的對(duì)齊更弱(即弱于其原生對(duì)齊,或弱于同一對(duì)象或類(lèi)型的另一聲明上的alignas
),那么程序非良構(gòu)。例如:
struct alignas(8) S {}; struct alignas(1) U { S s; }; // 錯(cuò)誤:如果沒(méi)有 alignas(1) 那么 U 的對(duì)齊將會(huì)是 8
- 無(wú)效的非零對(duì)齊:無(wú)效的非零對(duì)齊,例如
alignas(3)
是非良構(gòu)的。同一聲明上,比其他alignas
弱的有效的非零對(duì)齊被忽略,始終忽略alignas(0)
。
五、alignof和alignas的結(jié)合使用
alignof
和alignas
可以結(jié)合使用,alignof
可以用來(lái)驗(yàn)證alignas
設(shè)置的對(duì)齊是否生效。例如:
#include <iostream> struct alignas(16) MyStruct { int x; double y; }; int main() { std::cout << "alignof(MyStruct): " << alignof(MyStruct) << std::endl; return 0; }
在上述代碼中,MyStruct
被指定為16字節(jié)對(duì)齊,通過(guò)alignof(MyStruct)
可以驗(yàn)證其對(duì)齊要求確實(shí)為16字節(jié)。運(yùn)行上述代碼,輸出結(jié)果如下:
alignof(MyStruct): 16
六、實(shí)際應(yīng)用場(chǎng)景
6.1 性能優(yōu)化
某些CPU架構(gòu)對(duì)未對(duì)齊訪問(wèn)支持不好,強(qiáng)制對(duì)齊可以提升性能。在多媒體處理、科學(xué)計(jì)算和游戲開(kāi)發(fā)等領(lǐng)域,正確的內(nèi)存對(duì)齊可以顯著提升數(shù)據(jù)處理速度。例如,在使用SIMD指令集時(shí),需要將數(shù)據(jù)對(duì)齊到指定的字節(jié)邊界,否則可能會(huì)導(dǎo)致性能下降。
6.2 跨平臺(tái)開(kāi)發(fā)
不同平臺(tái)的默認(rèn)對(duì)齊可能不同,通過(guò)alignof
可以統(tǒng)一判斷,使用alignas
可以確保在不同平臺(tái)上都能滿(mǎn)足特定的對(duì)齊要求。例如,在進(jìn)行跨平臺(tái)的數(shù)據(jù)傳輸時(shí),為了保證數(shù)據(jù)的一致性和正確性,需要對(duì)數(shù)據(jù)進(jìn)行統(tǒng)一的對(duì)齊處理。
6.3 內(nèi)存池設(shè)計(jì)
分配內(nèi)存時(shí)要考慮對(duì)齊,確保不同類(lèi)型都能正確放置。在內(nèi)存池設(shè)計(jì)中,使用alignas
可以保證分配的內(nèi)存塊滿(mǎn)足特定的對(duì)齊要求,提高內(nèi)存的使用效率。例如:
#include <iostream> #include <cstddef> // 自定義內(nèi)存池類(lèi) class MemoryPool { public: MemoryPool(std::size_t blockSize, std::size_t align) : blockSize_(blockSize), align_(align) { // 分配內(nèi)存 pool_ = new char[blockSize_]; // 調(diào)整內(nèi)存地址以滿(mǎn)足對(duì)齊要求 char* alignedPool = reinterpret_cast<char*>(std::align(align_, blockSize_, pool_, blockSize_)); if (!alignedPool) { throw std::bad_alloc(); } current_ = alignedPool; } ~MemoryPool() { delete[] pool_; } void* allocate(std::size_t size) { if (current_ + size <= pool_ + blockSize_) { void* result = current_; current_ += size; return result; } return nullptr; } private: char* pool_; char* current_; std::size_t blockSize_; std::size_t align_; }; int main() { // 創(chuàng)建一個(gè)1024字節(jié)、16字節(jié)對(duì)齊的內(nèi)存池 MemoryPool pool(1024, 16); // 從內(nèi)存池中分配一個(gè)32字節(jié)的內(nèi)存塊 void* ptr = pool.allocate(32); if (ptr) { std::cout << "Allocated memory address: " << ptr << std::endl; } else { std::cout << "Memory allocation failed." << std::endl; } return 0; }
在上述代碼中,MemoryPool
類(lèi)用于管理一個(gè)內(nèi)存池,通過(guò)std::align
函數(shù)調(diào)整內(nèi)存地址以滿(mǎn)足對(duì)齊要求,確保分配的內(nèi)存塊是對(duì)齊的。
6.4 與硬件通信
在與硬件直接交互的編程中,如驅(qū)動(dòng)開(kāi)發(fā)或嵌入式系統(tǒng)編程,內(nèi)存對(duì)齊也是一個(gè)必須考慮的因素。例如,DMA(直接內(nèi)存訪問(wèn))或寄存器訪問(wèn)時(shí)通常有嚴(yán)格的對(duì)齊要求,使用alignas
可以確保數(shù)據(jù)滿(mǎn)足硬件的對(duì)齊要求,避免出現(xiàn)訪問(wèn)錯(cuò)誤。
七、總結(jié)
alignof
和alignas
是C++11中非常有用的特性,它們?yōu)殚_(kāi)發(fā)者提供了對(duì)內(nèi)存對(duì)齊的直接控制。alignof
用于查詢(xún)類(lèi)型或變量的對(duì)齊要求,alignas
用于指定變量或類(lèi)型的最小對(duì)齊要求。合理使用alignof
和alignas
可以提高程序的性能,特別是在需要高性能優(yōu)化的代碼中,如多媒體處理、科學(xué)計(jì)算和游戲開(kāi)發(fā)等領(lǐng)域。同時(shí),在跨平臺(tái)開(kāi)發(fā)、內(nèi)存池設(shè)計(jì)和與硬件通信等場(chǎng)景中,alignof
和alignas
也能發(fā)揮重要作用。在使用alignof
和alignas
時(shí),需要注意其語(yǔ)法規(guī)則和使用限制,以確保代碼的正確性和可移植性。希望本文能夠幫助你深入理解和掌握C++11中alignof
和alignas
的使用方法。
到此這篇關(guān)于C++11中alignof和alignas的入門(mén)的文章就介紹到這了,更多相關(guān)C++11 alignof和alignas內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
用c語(yǔ)言實(shí)現(xiàn)和平精英的完整代碼
這篇文章主要介紹了用c語(yǔ)言實(shí)現(xiàn)和平精英的完整代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04Qt實(shí)現(xiàn)自定義日志類(lèi)的示例代碼
這篇文章主要為大家詳細(xì)介紹了使用 qInstallMessageHandler() 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的日志工具,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以學(xué)習(xí)一下2023-12-12C++消息隊(duì)列(定義,結(jié)構(gòu),如何創(chuàng)建,發(fā)送與接收)
這篇文章主要介紹了C++消息隊(duì)列(定義,結(jié)構(gòu),如何創(chuàng)建,發(fā)送與接收),消息隊(duì)列是一種先進(jìn)先出的隊(duì)列型數(shù)據(jù)結(jié)構(gòu),實(shí)際上是系統(tǒng)內(nèi)核中的一個(gè)內(nèi)部鏈表2022-08-08C語(yǔ)言簡(jiǎn)明講解三目運(yùn)算符和逗號(hào)表達(dá)式的使用
三目運(yùn)算符,又稱(chēng)條件運(yùn)算符,它是唯一有3個(gè)操作數(shù)的運(yùn)算符,有時(shí)又稱(chēng)為三元運(yùn)算符。三目運(yùn)算符的結(jié)合性是右結(jié)合的;逗號(hào)表達(dá)式,是c語(yǔ)言中的逗號(hào)運(yùn)算符,優(yōu)先級(jí)別最低,它將兩個(gè)及其以上的式子聯(lián)接起來(lái),從左往右逐個(gè)計(jì)算表達(dá)式,整個(gè)表達(dá)式的值為最后一個(gè)表達(dá)式的值2022-04-04C++ STL入門(mén)教程(7) multimap、multiset的使用
這篇文章主要介紹了C++ STL入門(mén)教程第七篇,multimap一對(duì)多索引,multiset多元集合的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08C++中內(nèi)存池的簡(jiǎn)單原理及實(shí)現(xiàn)詳解
內(nèi)存池的思想是,在真正使用內(nèi)存之前,預(yù)先申請(qǐng)分配一定數(shù)量、大小預(yù)設(shè)的內(nèi)存塊留作備用。本文主要來(lái)和大家聊聊內(nèi)存池的簡(jiǎn)單原理及實(shí)現(xiàn),希望對(duì)大家有所幫助2023-03-03C/C++讀寫(xiě)JSON數(shù)據(jù)的詳細(xì)過(guò)程記錄
JSON文件無(wú)論是在web開(kāi)發(fā)、客戶(hù)端開(kāi)發(fā)、服務(wù)端等開(kāi)發(fā)中都是應(yīng)用比較廣泛的的第一種輕量級(jí)數(shù)據(jù)交換格式,非常方便閱讀和編寫(xiě),下面這篇文章主要給大家介紹了關(guān)于C/C++讀寫(xiě)JSON數(shù)據(jù)的詳細(xì)過(guò)程,需要的朋友可以參考下2023-04-04利用Matlab實(shí)現(xiàn)圖像亮度分布統(tǒng)計(jì)圖
這篇文章主要介紹了如何利用Matlab實(shí)現(xiàn)圖像亮度分布統(tǒng)計(jì)圖的繪制,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Matlab有一定的幫助,感興趣的可以了解一下2022-05-05