我在作業系統課程中遇到了這個問題。這是 6.828(作業系統)在線課程的代碼。它旨在讓學習者練習 C 編程語言中的指標。
#include <stdio.h>
#include <stdlib.h>
void
f(void)
{
int a[4];
int *b = malloc(16);
int *c;
int i;
printf("1: a = %p, b = %p, c = %p\n", a, b, c);
c = a;
for (i = 0; i < 4; i )
a[i] = 100 i;
c[0] = 200;
printf("2: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
a[0], a[1], a[2], a[3]);
c[1] = 300;
*(c 2) = 301;
3[c] = 302;
printf("3: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
a[0], a[1], a[2], a[3]);
c = c 1;
*c = 400;
printf("4: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
a[0], a[1], a[2], a[3]);
c = (int *) ((char *) c 1);
*c = 500;
printf("5: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
a[0], a[1], a[2], a[3]);
b = (int *) a 1;
c = (int *) ((char *) a 1);
printf("6: a = %p, b = %p, c = %p\n", a, b, c);
}
int
main(int ac, char **av)
{
f();
return 0;
}
我將它復制到一個檔案并使用 gcc 編譯它,然后我得到了這個輸出:
$ ./pointer
1: a = 0x7ffd3cd02c90, b = 0x55b745ec72a0, c = 0x7ffd3cd03079
2: a[0] = 200, a[1] = 101, a[2] = 102, a[3] = 103
3: a[0] = 200, a[1] = 300, a[2] = 301, a[3] = 302
4: a[0] = 200, a[1] = 400, a[2] = 301, a[3] = 302
5: a[0] = 200, a[1] = 128144, a[2] = 256, a[3] = 302
6: a = 0x7ffd3cd02c90, b = 0x7ffd3cd02c94, c = 0x7ffd3cd02c91
我可以很容易地理解 1,2,3,4 的輸出。但是我很難理解 5 的輸出。特別是為什么 a[1] = 128144 和 a[2] = 256?
看來這個輸出是
c = (int *) ((char *) c 1);
*c = 500;
我很難理解代碼的功能c = (int *) ((char *) c 1)。
c是定義的指標int *c。a在第 5 行的輸出之前, c通過c = aand指向陣列的第二個地址c = c 1。 那么(char *) cand是什么意思呢?((char *) c 1)(int *) ((char *) c 1)
uj5u.com熱心網友回復:
盡管根據標準這是未定義的行為,但它在“古代 C”中具有明確的含義,并且它在您正在使用的機器/編譯器上顯然以這種方式作業。
首先,它轉換c為 a (char *),這意味著指標算術將以sizeof(char)(即一個位元組)為單位而不是sizeof(int). 然后它增加一個位元組。然后它將結果轉換回(int *). 結果是一個 int 指標,它現在指向一個比以前高一個位元組的地址。由于c是事先指向的a[1],之后*c = 500會寫入 的最后三個位元組a[1]和 的第一個位元組a[2]。
在許多機器上(但不是 x86),這是完全違法的事情。像這樣的未對齊訪問只會使您的程式崩潰。C 標準更進一步說,該代碼可以做任何事情:當編譯器看到它時,它可以生成崩潰的代碼,什么都不做,寫入完全不相關的記憶體位,或者導致一個小 gnome 彈出用木槌敲擊你的顯示幕的一側。但是,有時在 UB 的情況下最容易做的事情也是簡單明了的事情,這就是其中一種情況。
您的課程材料試圖向您展示數字是如何存盤在記憶體中的,以及如何根據您告訴 CPU 的內容以不同的方式解釋相同的位元組。您應該本著這種精神接受它,而不是作為撰寫體面 C 的指南。
uj5u.com熱心網友回復:
這是未定義行為的結果。您呼叫未定義的行為是因為您取消參考空指標(對于陣列 a)并且陣列大小為零(對于陣列 b) - 對于這種情況,這等效于 c= a; b = 0; c = (int *) ((char *) c 1)。這應該會觸發一個警告,這就是為什么我還在上面的示例中添加了 -Wall -pedantic -std=c99 的原因。
回答您關于 (char *) c 和 ((char *) c 1) 的問題。
(char *) c:由于 c 是一個指標,所以 c->type 是 int *(指向 int 的指標)。這使得 c->type 具有 char * 型別。您獲取陣列 c 中第二個元素的地址并將其分配給 a。因此,c->type 就是 char *(陣列 c 中第二個元素的地址)。因此 c[0](索引 0)是陣列 c 中的第一個元素。
((char *) c 1) - c 1 = &c[1]. c[0] 1 = c[1] (first element of the array c 1).
uj5u.com熱心網友回復:
在第一個輸出中,c是指向一個隨機地址。
之后c = a;,c指向a,所以當你改變c[0], c[1], *(c 2), 3[c]a的值時,a的值也會相應改變。
在以下行:
c = c 1;
c現在指向,a[1]地址將是0x7ffd3cd02c94.
現在轉到您要求的行:c = (int *) ((char *) c 1);它將執行以下操作:
- 轉換為仍然指向相同地址
c的指標型別char0x7ffd3cd02c94。 - 增加指標 1,所以現在地址是
0x7ffd3cd02c95 - 再次將新地址分配給
c(int *)。
在該命令之前,c將指向地址:0x7ffd3cd02c94-0x7ffd3cd02c97。但在那之后,地址將是:0x7ffd3cd02c95-0x7ffd3cd02c98。這就是值為[5][![在此處輸入影像描述][1]][1]的原因
現在很清楚為什么值會像您觀察到的那樣發生變化。
注意:這對于小端系統是正確的。對于大端,結果會有點不同。對于某些不允許 UNALIGNED 訪問的嵌入式平臺,您應該在該行遇到例外。[1]:https ://i.stack.imgur.com/eU0Tb.png
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/518353.html
標籤:C指针
