計算機內(nèi)存是以字節(jié)(Byte)為單位劃分的,理論上CPU可以訪問任意編號的字節(jié),但實際情況并非如此。
CPU 通過地址總線來訪問內(nèi)存,一次能處理幾個字節(jié)的數(shù)據(jù),就命令地址總線讀取幾個字節(jié)的數(shù)據(jù)。32 位的 CPU 一次可以處理4個字節(jié)的數(shù)據(jù),那么每次就從內(nèi)存讀取4個字節(jié)的數(shù)據(jù);少了浪費主頻,多了沒有用。64位的處理器也是這個道理,每次讀取8個字節(jié)。
以32位的CPU為例,實際尋址的步長為4個字節(jié),也就是只對編號為 4 的倍數(shù)的內(nèi)存尋址,例如 0、4、8、12、1000 等,而不會對編號為 1、3、11、1001 的內(nèi)存尋址。如下圖所示:
這樣做可以以最快的速度尋址:不遺漏一個字節(jié),也不重復(fù)對一個字節(jié)尋址。
對于程序來說,一個變量最好位于一個尋址步長的范圍內(nèi),這樣一次就可以讀取到變量的值;如果跨步長存儲,就需要讀取兩次,然后再拼接數(shù)據(jù),效率顯然降低了。
例如一個 int 類型的數(shù)據(jù)在內(nèi)存中占據(jù)4個字節(jié),如果地址為 8,那么很好辦,對編號為 8 的內(nèi)存尋址一次就可以。如果編號為 10,就比較麻煩,CPU需要先對編號為 8 的內(nèi)存尋址,讀取4個字節(jié),得到該數(shù)據(jù)的前半部分,然后再對編號為 12 的內(nèi)存尋址,讀取4個字節(jié),得到該數(shù)據(jù)的后半部分,再將這兩部分拼接起來,才能取得數(shù)據(jù)的值。
將一個數(shù)據(jù)盡量放在一個步長之內(nèi),避免跨步長存儲,這稱為內(nèi)存對齊。在32位編譯模式下,默認以4字節(jié)對齊;在64位編譯模式下,默認以8字節(jié)對齊。
關(guān)于Go語言的內(nèi)存對齊示例請看這里:
Go 內(nèi)存對齊-結(jié)構(gòu)體