C語言記憶體位址基礎(ZZ)

2021-06-29 12:21:19 字數 4596 閱讀 7649

從計算機記憶體的角度思考c語言中的一切東東,是挺有幫助的。我們可以把計算機記憶體想象成乙個位元組陣列,記憶體中每乙個位址表示 1 位元組。比方說我們的電腦有 4k 記憶體,那這個記憶體陣列將會有 4096 個元素。當我們談論乙個儲存位址的指標時,就當相於我們在談論乙個儲存著該記憶體陣列某個元素索引的指標。逆向引用某個指標,將會得到陣列中該索引所指向的值。這一切當然都是謊言。作業系統對記憶體的管理要遠比這複雜。記憶體不一定連續,也不一定按順序處理。但前面的模擬是一種討論c語言記憶體的簡單方式。

如果對『指標』、『位址』和『逆向引用』感到混亂,請看《c語言指標5分鐘教程》。// 譯註:「dereferencing」 的譯法比較多,本文採用了「逆向引用」。

假設我們的計算機有 4k 的記憶體,下乙個開放位址的索引是2048。我們宣告乙個新的字元變數i='a'。當該變數所獲得的記憶體放置了它的值,變數的名字也與記憶體中的該位置關聯,我們的字元i就獲得了乙個儲存在2048位置的值。該字元是單位元組的因此它只占用了索引為 2048 的位置。如果我們對 i 變數使用位址操作符(&),它將返回到索引為2048的位置。如果這個變數是另一種型別,比如是 int,它將占用4位元組,在陣列中占用索引為 2048-2051 的位置。使用位址操作符仍將返回索引2048的位置,因為 int 型即便占用了 4 位元組,但它開始於 2048 位置。我們看乙個例子:

// intialize a char variable, print its address and the next address

char charvar = '\0';

printf("address of charvar = %p\n", (void *)(&charvar));

printf("address of charvar - 1 = %p\n", (void *)(&charvar - 1));

printf("address of charvar + 1 = %p\n", (void *)(&charvar + 1));

// intialize an int variable, print its address and the next address

int intvar = 1;

printf("address of intvar = %p\n", (void *)(&intvar));

printf("address of intvar - 1 = %p\n", (void *)(&intvar - 1));

printf("address of intvar + 1 = %p\n", (void *)(&intvar + 1));

執行將得到如下的輸出:

address of charvar = 0x7fff9575c05f

address of charvar - 1 = 0x7fff9575c05e

address of charvar + 1 = 0x7fff9575c060

address of intvar = 0x7fff9575c058

address of intvar - 1 = 0x7fff9575c054

address of intvar + 1 = 0x7fff9575c05c

在第乙個例子的1-5行中,我們宣告了乙個字元變數,並列印輸出該字元的位址,然後列印了記憶體中位於該變數前後的兩個位址。我們是通過使用&操作符並+1或-1來獲取前後兩個位址的。在7-11行的第二個例子中我們做了差不多的事,除了宣告了乙個int型變數,列印出它的位址以及緊鄰它前後的位址。

在輸出中,我們看到位址是 16 進製的。更值得注意的是,字元的位址前後相差1位元組。int 型變數位址前後相差四位元組。記憶體位址的演算法、指標的演算法、都是根據所引用的型別的大小的。乙個給定的型別的大小是依賴於平台的,我們這個例子中的char是1位元組,int是四位元組。將字元的位址-1是改位址前的位址,而將int型位址-1是該位址前4個的位址。

在例子中,我們是用位址操作符來獲取變數的位址,這和使用表示變數位址的指標是一樣的效果。

在c語言中,陣列是相鄰的記憶體區域,它儲存了大量相同資料型別的值(int、long、*char等等)。很多程式設計師第一次用c時,會將陣列當做指標。那是不對的。指標儲存乙個簡單的記憶體位址,而乙個陣列是一塊儲存多個值的連續的記憶體區域。

// initialize an array of ints

int numbers[5] = ;

int i = 0;

// print the address of the array variable

printf("numbers = %p\n", numbers);

// print addresses of each array index

do while(i < 5);

// print the size of the array

printf("sizeof(numbers) = %lu\n", sizeof(numbers));

執行將得到如下的輸出:

numbers = 0x7fff0815c0e0

numbers[0] = 0x7fff0815c0e0

numbers[1] = 0x7fff0815c0e4

numbers[2] = 0x7fff0815c0e8

numbers[3] = 0x7fff0815c0ec

numbers[4] = 0x7fff0815c0f0

sizeof(numbers) = 20

在這個例子中,我們初始化了乙個含有 5 個 int 元素的陣列,我們列印了陣列本身的位址,注意我們沒有使用位址操作符 & 。這是因為陣列變數已經代表了陣列首元素的位址。你會看到陣列的位址與陣列首元素的位址是一樣的。然後我們遍歷這個陣列並列印每個元素的記憶體位址。在我們的計算機中 int 是四個位元組的,陣列記憶體是連續的,因此每個int型元素位址之間相差4。

在最後一行,我們列印了陣列的大小,陣列的大小等於sizeof(type)乘上陣列元素的數量。這裡的陣列有5個int型變數,每乙個占用4位元組,因此整個陣列大小為20位元組。

在c語言中,結構體一般是連續的記憶體區域,但也不一定是絕對連續的區域。和陣列類似,它們能儲存多種資料型別,但不同於陣列的是,它們能儲存不同的資料型別。

struct measure ;

// declare and populate the struct

struct measure ball;

ball.category = 'c';

ball.width = 5;

ball.height = 3;

// print the addresses of the struct and its members

printf("address of ball = %p\n", (void *)(&ball));

printf("address of ball.category = %p\n", (void *)(&ball.category));

printf("address of ball.width = %p\n", (void *)(&ball.width));

printf("address of ball.height = %p\n", (void *)(&ball.height));

// print the size of the struct

printf("sizeof(ball) = %lu\n", sizeof(ball));

執行後的輸出結果如下:

address of ball = 0x7fffd1510060

address of ball.category = 0x7fffd1510060

address of ball.width = 0x7fffd1510064

address of ball.height = 0x7fffd1510068

sizeof(ball) = 12

在這個例子中我們定義了乙個結構體measure,然後宣告了該結構體的乙個例項ball,我們賦值給它的width、height以及category成員,然後列印出ball的位址。與陣列類似,結構體也代表了它首元素的位址。然後列印了它每乙個成員的位址。category是第乙個成員,它與ball具有相同的位址。width後面是height,它們都具有比category更高的位址。

你可能會想因為category是乙個字元,而字元型變數占用1位元組,因此width的位址應該比開始出高1個位元組。從輸出來看這不對。 根據c99標準(§6.7.2.1),為邊界對齊,結構體可以給成員增加填充位元組。它不會記錄資料成員,但會增加額外的位元組。在實際中,大多數的編譯器會使結構體中的每個成員與結構體最大的成員有相同大小,

在我們的例子中,你可以看到char實際上占用4位元組,整個struct占用12個位元組。都發生了什麼?

1.struct變數指向struct首元素的位址

2.不要去假設乙個結構體的成員相對於另外乙個成員有多少記憶體偏移量,結構體成員之間可能有邊界位元組,或者編譯器也可能將它們放在不連續的記憶體空間中。使用位址操作符&來獲得成員的位址

3.使用sizeof(struct instance)來獲得struct的總大小,不能假設它是各個成員大小的大小總和,也許還有填充位元組。

**:

C語言記憶體位址

任務清單 c語言在記憶體中一共分為5個區域 記憶體棧區 存放區域性變數名 由編譯器自動分配釋放,存放函式的引數值,區域性變數的值等,函式呼叫結束後釋放記憶體空間。通常是用於那些在編譯期間就能確定儲存大小的變數的儲存區,用於在函式作用域內建立,在離開作用域後自動銷毀的變數的儲存區。通常是區域性變數,函...

C語言基礎之 記憶體位址分配

一.記憶體劃分 位址由高到低 1.棧區 棧區的資料以棧的形式進行儲存,特點 先進後出 函式體內存放的區域性變數存放在棧區,由系統分配空間和系統釋放.開發人員不需要關心如何為區域性變數分配空間.int a 10 printf p n a 2.堆區由開發人員手動申請,手動釋放,唯一一塊由開發人員操作的區...

C語言記憶體位址對齊詳解

什麼是位址對齊?現代計算機中記憶體空間都是按照位元組 byte 劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何位址開始,但實際情況是在訪問特定變數的時候經常在特定的記憶體位址訪問,這就需要各型別資料按照一定的規則在空間上排列,而不是順序的乙個接乙個的排列,這就是對齊。為什麼要位址對齊?對齊的...