程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 深刻懂得c/c++ 內存對齊

深刻懂得c/c++ 內存對齊

編輯:關於C++

深刻懂得c/c++ 內存對齊。本站提示廣大學習愛好者:(深刻懂得c/c++ 內存對齊)文章只能為提供參考,不一定能成為您想要的結果。以下是深刻懂得c/c++ 內存對齊正文


內存對齊,memory alignment.為了進步法式的機能,數據構造(特別是棧)應當盡量地在天然界限上對齊。緣由在於,為了拜訪未對齊的內存,處置器須要作兩次內存拜訪;但是,對齊的內存拜訪僅須要一次拜訪。
內存對齊普通講就是cpu access memory的效力(進步運轉速度)和精確性(在一些前提下,假如沒有對齊會招致數據分歧步景象).依附cpu,平台和編譯器的分歧.一些cpu請求較高(這句話說的禁絕確,然則確切依附cpu的分歧),而有些平台曾經優化內存對齊成績,分歧編譯器的對齊模數分歧.總的來講內存對齊屬於編譯器的成績.

普通情形下不須要理睬內存對齊成績,內存對齊是編譯器的工作.但碰著一些成績上照樣須要懂得這個概念.究竟c/c++值直接操作內存的說話.須要懂得法式在內存中的散布和運轉道理.

總之一句話就是:不要讓代碼依附內存對齊.


1.緣由:為何須要內存對齊.
1、平台緣由(移植緣由):不是一切的硬件平台都能拜訪隨意率性地址上的隨意率性數據的;某些硬件平台只能在某些地址處取某些特定類型的數據,不然拋出硬件異常。

2、機能緣由:數據構造(特別是棧)應當盡量地在天然界限上對齊。緣由在於,為了拜訪未對齊的內存,處置器須要作兩次內存拜訪;而對齊的內存拜訪僅須要一次拜訪。


2.內存對齊的規矩和典范
講述內存對齊之前先看下各類類型的年夜小,和編譯器和字長有關詳細在此不多論述.
詳細帖子:http://blog.csdn.net/lyl0625/article/details/7350045
成員的內存分派紀律:從構造體的首地址開端向後順次為每一個成員尋覓第一個知足前提的首地址x,該前提是x % N = 0,而且全部構造的長度必需為各個成員所應用的對齊參數中最年夜的誰人值的最小整數倍,不敷就補空字節。
構造體中一切成員的對齊參數N的最年夜值稱為構造體的對齊參數。

1、數據成員對齊規矩:構造(struct)(或結合(union))的數據成員,第一個數據成員放在offset為0的處所,今後每一個數據成員的對齊依照#pragma pack指定的數值(或默許值)和這個數據成員類型長度中,比擬小的誰人停止。在上一個對齊後的處所開端尋覓能被以後對齊數值整除的地址.
2、構造(或結合)的全體對齊規矩:在數據成員完成各自對齊以後,構造(或結合)自己也要停止對齊.重要表現在,最初一個元素對齊後,前面能否彌補空字節,假如彌補,彌補若干.對齊將依照#pragma pack指定的數值(或默許值)和構造(或結合)最年夜數據成員類型長度中,比擬小的誰人停止。
3、聯合1、2顆揣摸:當#pragma pack的n值等於或跨越一切數據成員類型長度的時刻,這個n值的年夜小將不發生任何後果。
兩點留意:數組,嵌套構造體.
數組:
對齊值為:min(數組元素類型,指定對齊長度).但數組中的元素是持續寄存,寄存時照樣依照數組現實的長度.
如char t[9],對齊長度為1,現實占用持續的9byte.然後依據下一個元素的對齊長度決議鄙人一個元素之前彌補若干byte.
嵌套的構造體:
假定
struct A
{
......
struct B b;
......
};
關於B構造體在A中的對齊長度為:min(B構造體的對齊長度,指定的對齊長度).
B構造體的對齊長度為:上述2中構造全體對齊規矩中的對齊長度.

例子:
VC++6.0中n 默許是8個字節,可以修正這個設定的對齊參數
也能夠采取指令:#pragma   pack(xx)掌握.

1.基本例子

#pragma   pack(n)
struct A
{
char   c; //1byte
double d; //8byte
short s; //2byte
int i; //4byte
};
int main(int argc, char* argv[])
{
A strua;
printf("%len:d\n",sizeof(A));
printf("%d,%d,%d,%d",&strua.c,&strua.d,&strua.s,&strua.i);
return 0;
}

1)n設置為8byte時
成果:len:24,
1245032,1245040,1245048,1245052
內存中成員散布以下:
strua.c分派在一個肇端於8的整數倍的地址1245032(為何是如許讀者先本身思慮,讀完就會明確),接上去要在strua.c以後分派strua.d,因為double為8字節,取N=min(8,8),8字節來對齊,所以從strua.c向後找第一個能被8整除的地址,所以取1245032+8得1245040, strua.s 為2byte小於參數n,所以N=min(2,8),即N=2,取2字節長度對齊,所以要從strua.d前面尋覓第一個能被2整除的地址來存儲strua.s,因為strua.d前面的地址為1245048可以被2整除,所以strua.s緊接著分派,如今來分派strua.i,int為4byte,小於指定對齊參數8byte,所以N=min(4,8)取N=4byte對齊,strua.s前面第一個能被4整除地址為1245048+4,所以在1245048+4的地位分派了strua.i,中央補空,同時因為一切成員的N值的最年夜值為8,所以全部構造長度為8byte的最小整數倍,即取24byte其他均補0.
因而該構造體的對齊參數就是8byte。
2)當對齊參數n設置為16byte時,成果同上,不再剖析
3)當對齊參數設置為4byte時
上例成果為:Len:20
1245036,1245040,1245048,1245052
內存中成員散布以下:
Strua.c肇端於一個4的整數倍的地址,接上去要在strua.c以後分派strua.d,因為strua.d長度為8byte,年夜於對齊參數4byte,所以N=min(8,4)取最小的4字節,所以向後找第一個能被4整除的地址來作為strua.d首地址,故取1245036+4,接著要在strua.d後分派strua.s,strua.s長度為2byte小於4byte,取N=min(2,4)2byte對齊,因為strua.d後的地址為1245048可以被2
整除,所以直接在strua.d前面分派,strua.i的長度為4byte,所以取N=min(4,4)4byte對齊,所以從strua.s向後找第一個能被4整除的地位即1245048+4來分派和strua.i,同時N的最年夜值為4byte,所以全部構造的長度為4byte的最小整數倍20byte
4)當對齊參數設置為2byte時
上例成果為:Len:16
1245040,1245042,1245050,1245052
Strua.c分派後,向後找一第一個能被2整除的地位來寄存strua.d,順次類推
5)1byte對齊時:
上例成果為:Len:15
1245040,1245041,1245049,1245051
此時,N=min(sizeof(成員),1),取N=1,因為1可以整除任何整數,所以各個成員順次分派,沒有間空.
6)當構造體成員為數組時,其實不是將全部數組當做一個成員來看待,而是將數組的每一個元素當一個成員來分派,其他分派規矩不變,如將上例的構造體改成:
struct A
{
char c; //1byte
double d; //8byte
short s; //2byte
char  szBuf[5];
};
對齊參數設置為8byte,則,運轉成果以下:
Len:24
1245032,1245040,1245048,1245050
Strua 的s分派後,接上去分派Strua 的數組szBuf[5],這裡要零丁分派它的每一個元素,因為是char類型,所以N=min(1,8),取N=1,所以數組szBuf[5]的元素順次分派沒有間隙。

看完上述的例子,根本分派的紀律和辦法應當曾經曉得.上面重要解釋數組,嵌套構造體,指針時的一些內存對齊成績.
最主要的是本身寫法式證實.

2.數組,嵌套.
測試情況:64位 ubuntu;g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3


#include <iostream>
#include <cstdio>
using namespace std;
#pragma pack(8)
struct Args
{
        char ch;
        double d;
        short st;
        char rs[9];
        int i;
} args;
struct Argsa
{
        char ch;
        Args test;
        char jd[10];
        int i;
}arga;

int main()
{
// cout <<sizeof(char)<<" "<<sizeof(double)<<" "<<sizeof(short)<<" "<<sizeof(int)<<endl;
//cout<<sizeof(long)<<" "<<sizeof(long long)<<" "<<sizeof(float)<<endl;
cout<<"Args:"<<sizeof(args)<<endl;
cout<<""<<(unsigned long)&args.i-(unsigned long)&args.rs<<endl;
cout<<"Argsa:"<<sizeof(arga)<<endl;
cout<<"Argsa:"<<(unsigned long)&arga.i -(unsigned long)&arga.jd<<endl;
cout<<"Argsa:"<<(unsigned long)&arga.jd-(unsigned long)&arga.test<<endl;
return 0;
}

輸入成果:
Args:32
10
Argsa:56
Argsa:12
Argsa:32

struct Args長度32 struct Argsa長度:56.
改成#pragma pack (16)成果一樣.
這個例子證實了三點:
對齊長度擅長struct中的類型長度最長的值時,設置的對齊長度等於無用.
數組對齊的長度是依照數構成員類型長度來比對的.
嵌套的構造體中,所包括的構造體的對齊長度是構造體的對齊長度.

3.指針.重要是由於32位和64位機尋址上
測試情況同2.(64位體系)


#include <iostream>
#include <cstdio>
#pragma pack(4)
using namespace std;

struct Args
{
        int i;
        double d;
        char *p;
        char ch;
        int *pi;
}args;
int main()
{   
        cout<<"args length:"<<sizeof(args)<<endl;
        cout<<(unsigned long)&args.ch-(unsigned long)&args.p<<endl;
        cout<<(unsigned long)&args.pi-(unsigned long)&args.ch<<endl;
        return 0;
}

設置pack為4時:
args length:32
8
4

設置pack為8時:
args length:40
8
8
看了上述內容,應當能剖析出來為何是這個成果.這裡不具體描寫.

3.分歧編譯器中內存對齊
VC 6.0上是8 byte

gcc 默許是8byte.測試版本gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
g++默許是8byte.測試版本g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
但查閱的材料說是gcc 默許是4,且不支撐pragma參數設定.測試的時刻gcc默許對齊為8byte且,支撐pragma參數.
測試過兩個分歧的例子,成果雷同.

4.甚麼時刻須要停止內存對齊.
普通情形下都不須要對編譯器停止的內存對齊規矩停止修正,由於如許會下降法式的機能,除非在以下兩種情形下:

(1)這個構造須要直接被寫入文件;

(2)這個構造需經由過程收集傳給其他法式;

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved