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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

C++中的内存对齐介绍

發(fā)布時間:2023/11/27 生活经验 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++中的内存对齐介绍 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

網(wǎng)上有很多介紹字節(jié)對齊或數(shù)據(jù)對齊或內(nèi)存對齊的文章,雖然名字不一樣,但是介紹的內(nèi)容大致都是相同的。這里以內(nèi)存對齊相稱。注:以下內(nèi)容主要來自網(wǎng)絡。

內(nèi)存對齊,通常也稱為數(shù)據(jù)對齊,是計算機對數(shù)據(jù)類型合法地址做出了一些限制,要求某種類型對象的地址必須是某個值K(通常使2、4、8、16、32或64)的倍數(shù)。

現(xiàn)代計算機中內(nèi)存空間都是按照byte劃分的,從理論上講似乎對任何類型的變量的訪問可以從任何地址開始,但實際情況是在訪問特定類型變量的時候經(jīng)常在特定的內(nèi)存地址訪問,這就需要各種類型數(shù)據(jù)按照一定的規(guī)則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。

內(nèi)存對齊原因:

(1). 平臺原因(移植原因):不是所有的硬件平臺都能訪問任意地址上的任意數(shù)據(jù)的;某些硬件平臺只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常。

(2). 性能原因:數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應該盡可能地在自然邊界上對齊。原因在于,為了訪問未對齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問;而對齊的內(nèi)存訪問僅需要一次訪問。

對齊值必須是2的冪次方,如1, 2, 4, 8, 16。如果一個變量按n字節(jié)對齊,那么該變量的起始地址必須是n的倍數(shù)。

每個特定平臺上的編譯器都有自己默認的”對齊系數(shù)”,可以通過設置#pragma pack(n),告訴編譯器,所有的對齊都是按照n的整數(shù)倍對齊。

在結(jié)構(gòu)體中,整個結(jié)構(gòu)的大小必須是其中最大字段大小的整數(shù)倍。

為了讓處理器快速讀寫內(nèi)存里面的數(shù)據(jù),默認情況,編譯器會把:

(1). 1個字節(jié)的變量,例如char類型的變量,放在任意地址的位置上;

(2). 2個字節(jié)的變量,例如short類型的變量,放在2的整數(shù)倍的地址上;

(3). 4個字節(jié)的變量,例如long/float類型的變量,放在4的整數(shù)倍地址上;

(4). 8個字節(jié)的變量,例如long long/uint64_t或double類型的變量,放在8的整數(shù)倍地址上;

(5). 16個字節(jié)的變量,放在8的整數(shù)倍地址上,因為默認的對齊方式是 8。

變量在內(nèi)存里面的順序,和定義變量的順序相同。為了符合對齊方式,就會在變量之間加入填充字節(jié)(padding),讓后面的變量放在按照對齊方式的規(guī)則的地址上

strcut/class/union內(nèi)存對齊規(guī)則:

1. 沒有#pragma pack宏的對齊規(guī)則:

(1). 結(jié)構(gòu)體的起始存儲位置必須是能夠被該結(jié)構(gòu)體中最大的數(shù)據(jù)類型所整除。

(2). 每個數(shù)據(jù)成員存儲的起始位置是自身大小的整數(shù)倍(比如int在32位機為4字節(jié),則int型成員要從4的整數(shù)倍地址開始存儲)。

(3). 結(jié)構(gòu)體總大小(也就是sizeof的結(jié)果),必須是該結(jié)構(gòu)體成員中最大的對齊模數(shù)的整數(shù)倍。若不滿足,會根據(jù)需要自動填充空缺的字節(jié)。

(4). 結(jié)構(gòu)體包含另一個結(jié)構(gòu)體成員,則被包含的結(jié)構(gòu)體成員要從其原始結(jié)構(gòu)體內(nèi)部最大對齊模數(shù)的整數(shù)倍地址開始存儲(比如struct a里存有struct b,b里有char,int,double等元素,那b應該從8的整數(shù)倍開始存儲)。

(5). 結(jié)構(gòu)體包含數(shù)組成員,比如char a[3],它的對齊方式和分別寫3個char是一樣的,也就是說它還是按一個字節(jié)對齊。如果寫:typedef char Array[3], Array這種類型的對齊方式還是按一個字節(jié)對齊,而不是按它的長度3對齊。

(6). 結(jié)構(gòu)體包含共用體成員,則該共用體成員要從其原始共用體內(nèi)部最大對齊模數(shù)的整數(shù)倍地址開始存儲。

2. 存在#pragma pack宏的對齊:

(1). #pragma pack (n) // 編譯器將按照n個字節(jié)對齊?

(2). #pragma pack () //取消自定義字節(jié)對齊方式

可以通過C++11中的alignas函數(shù)來指定類型、對象或變量按多少字節(jié)對齊,可以通過alignof函數(shù)來判斷類型、對象或變量是按多少字節(jié)對齊的。

下面是從其他文章中copy的測試代碼,詳細內(nèi)容介紹可以參考對應的reference:

memory_alignment.cpp內(nèi)容如下:

#include "memory_alignment.hpp"
#include <iostream>
#include <cstdlib>//#pragma pack(1) // use #pragma pack set memory alignmentnamespace memory_alignment_ {//
int test_memory_alignment_1()
{{ // struct	typedef struct A {char c;} A;typedef struct B {int i;} B;typedef struct C {double d;} C;typedef struct D {char c; int i;} D;typedef struct E {char* p;} E; // 32bits is 4, 64bits is 8typedef struct F {char* p; int* p2;} F;typedef struct G {char c1; char c2; char c3;} G;typedef struct H {char c; int* p;} H;typedef struct I {char c; int* p; int i;} I;typedef struct J {char c; int i; int* p;} J;typedef struct K {} K; // C++ size is 1, but C is 0fprintf(stdout, "size: A: %d, B: %d, C: %d, D: %d, E: %d, F: %d, G: %d, H: %d, I: %d, J: %d, K: %d\n",sizeof(A), sizeof(B), sizeof(C), sizeof(D), sizeof(E), sizeof(F), sizeof(G), sizeof(H), sizeof(I), sizeof(J), sizeof(K));fprintf(stdout, "size: short: %d, long: %d, float: %d, long long: %d, double: %d, uint64_t: %d\n",sizeof(short), sizeof(long), sizeof(float), sizeof(long long), sizeof(double), sizeof(uint64_t));
}return 0;
}//
// reference: https://stackoverflow.com/questions/17091382/memory-alignment-how-to-use-alignof-alignas
int test_memory_alignment_2()
{
{// alignas: 類型或?qū)ο蠡蜃兞堪粗付ǖ淖止?jié)對齊// Alignment of 16 means that memory addresses that are a multiple of 16 are the only valid addresses.alignas(16) int a[4];alignas(1024) int b[4];fprintf(stdout, "address: %p\n", a);fprintf(stdout, "address: %p\n", b);// alignof: 查詢指定類型的對齊要求,返回size_t類型值if (alignof(a) != 16 || (unsigned long long)a % 16 != 0) {fprintf(stderr, "a must be 16 byte aligned.\n");return -1;}if (alignof(b) != 1024 || (unsigned long long)b % 1024 != 0) {fprintf(stderr, "b must be 1024 byte aligned.\n");return -1;}
}{// every object of type sse_t will be aligned to 16-byte boundarystruct alignas(16) sse_t { float sse_data[4]; };// the array "cacheline" will be aligned to 128-byte boundaryalignas(128) char cacheline[128];
}return 0;
}//
// reference: https://en.cppreference.com/w/cpp/language/alignof
struct Foo {int   i;float f;char  c;
};struct Empty {};struct alignas(64) Empty64 {};int test_memory_alignment_3()
{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;
}//
// reference: https://msdn.microsoft.com/en-us/library/dn956973.aspx
int test_memory_alignment_4()
{struct x_ {char a;     // 1 byte  int b;      // 4 bytes  short c;    // 2 bytes  char d;     // 1 byte  } MyStruct;// The compiler pads this structure to enforce alignment naturally.// The following code example shows how the compiler places the padded structure in memory:Copy// Shows the actual memory layout  /*struct x_ {char a;            // 1 byte  char _pad0[3];     // padding to put 'b' on 4-byte boundary  int b;            // 4 bytes  short c;          // 2 bytes  char d;           // 1 byte  char _pad1[1];    // padding to make sizeof(x_) multiple of 4  } MyStruct; */// 1. Both declarations return sizeof(struct x_) as 12 bytes.// 2. The second declaration includes two padding elements:// 3. char _pad0[3] to align the int b member on a four-byte boundary// 4. char _pad1[1] to align the array elements of the structure struct _x bar[3];// 5. The padding aligns the elements of bar[3] in a way that allows natural access.return 0;
}} // namespace memory_alignment_

CMakeLists.txt內(nèi)容如下:

PROJECT(CppBaseTest)
CMAKE_MINIMUM_REQUIRED(VERSION 3.0)# 支持C++11
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wall -O2 -std=c11")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}  -g -Wall -O2 -std=c++11")
# 支持C++14, when gcc version > 5.1, use -std=c++14 instead of c++1y
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}  -g -Wall -O2 -std=c++1y")MESSAGE(STATUS "project source dir: ${PROJECT_SOURCE_DIR}")
SET(PATH_SRC_FILES ${PROJECT_SOURCE_DIR}/./../../demo/CppBaseTest)
MESSAGE(STATUS "path src files: ${PATH_SRC_FILES}")# 指定頭文件的搜索路徑
INCLUDE_DIRECTORIES(${PATH_SRC_FILES})# 遞歸查詢所有匹配的文件:*.cpp
FILE(GLOB_RECURSE CPP_LIST ${PATH_SRC_FILES}/*.cpp)
FILE(GLOB_RECURSE C_LIST ${PATH_SRC_FILES}/*.c)
#MESSAGE(STATUS "cpp list: ${C_LIST}")# 編譯可執(zhí)行程序
ADD_EXECUTABLE(CppBaseTest ${CPP_LIST} ${C_LIST})
# 用來為target添加需要鏈接的共享庫,指定工程所用的依賴庫,包括動態(tài)庫和靜態(tài)庫
TARGET_LINK_LIBRARIES(CppBaseTest pthread)

build.sh腳本內(nèi)容如下:

#! /bin/bashreal_path=$(realpath $0)
dir_name=`dirname "${real_path}"`
echo "real_path: ${real_path}, dir_name: ${dir_name}"new_dir_name=${dir_name}/build
mkdir -p ${new_dir_name}
cd ${new_dir_name}
cmake ..
makecd -

編譯及測試方法如下:首先執(zhí)行build.sh,然后再執(zhí)行./build/CppBaseTest即可。

GitHub: https://github.com/fengbingchun/Messy_Test??

總結(jié)

以上是生活随笔為你收集整理的C++中的内存对齐介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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