[英]C language: find output of given program
所以我有這個main
:
#define NUM 5
int main()
{
int a[NUM]={20,-90,450,-37,87};
int *p;
for (p=a; (char *)p < ((char *)a + sizeof(int) * NUM); ) //same meaning: for (p=a; p<a+NUM;)
*p++ = ++*p < 60 ? *p : 0; //same meaning: *(p++)=++(*p)<60?*p:0;
for(p=a; (char *)p < ((char *)a + sizeof(int) * NUM); )
printf("\n %d ", *p++);
return 0;
}
我需要找到輸出是什么。 因此,在嘗試理解而沒有任何想法后,我運行它,這是輸出:
21
-89
0
-36
0
所以我很樂意解釋如何解決這類問題(我很快就要考試了,我可能會看到這類問題......)
編輯:
一開始我想了解第一個for
語句在做什么:
這跳1個integer
? 這塊里面有什么?
*p++
和++*p
什么不同
問題類似於為什么這些構造(使用 ++)在 C 中是未定義的行為? 盡管由於?:
運算符內的(微妙)序列點而不是完全重復。
由於程序包含未定義的行為,因此沒有可預測的輸出。
盡管由於?:
運算符的內部序列點,子表達式++*p
與*p
相比以明確定義的方式排序,但對於子表達式的其他組合,情況並非如此。 最值得注意的是,未指定=
操作數的計算順序:
C11 6.5.15/3:
操作數的評估是無序的。
*p++
與++*p
無關。 子表達式的求值順序未指定,並且由於對同一變量存在多個未排序的副作用,因此行為未定義。
類似地, *p++
與*p
無關。 這也會導致未定義的行為。
總結:代碼已損壞且充滿錯誤。 任何事情都可能發生。 誰給你的任務是不稱職的。
一開始我想了解第一個 for 語句在做什么
這就是人們所說的代碼混淆……困難的部分顯然是這個:
(char *)p < ((char *)a+sizeof(int)*NUM);
好的,我們將 p 轉換為指向 char 的指針,然后將其與從數組 a 中檢索到的另一個指針進行比較,該指針指向經過 a 的第一個元素: sizeof(int)*NUM
是數組的大小 - 我們可以得到更多只需有sizeof(a)
就很容易,所以(char*)p < (char*)a + sizeof(a)
請注意,如果指針不指向同一個數組或指向后者的末尾(但在本例中確實如此),則比較與 (in-)equality 不同的指針是未定義的行為。
通常,人們會將這種比較作為p < a + sizeof(a)/sizeof(*a)
(或sizeof(a)/sizeof(a[0])
,如果您願意)。
*p++
遞增指針並在之后取消引用它,它是p = p + 1; *p = ...
縮寫p = p + 1; *p = ...
p = p + 1; *p = ...
. ++*p
,另一方面,首先取消引用指針並增加它指向的值(注意與*++p
的區別,另一個變體 - 你能自己得到嗎?),即它相當於*p = *p + 1
。
整行*p++ = ++*p<60 ? *p : 0;
*p++ = ++*p<60 ? *p : 0;
然后應執行以下操作:
*p
的值*p
p
但是,這是未定義的行為,因為在p
讀寫訪問之間沒有序列點; 你不知道是先評估左邊還是右邊,在前一種情況下,我們會分配a[i] = ++a[i + 1]
,在后一種情況下, a[i] = ++a[i]
! 你可能用另一個編譯器得到了不同的輸出!!!
然而,這只是兩個最有可能的輸出——實際上,如果陷入未定義的行為,任何事情都可能發生,編譯器可能只是忽略有問題的代碼片段,決定什么都不做(只是從主右退出第一條指令),程序可能會崩潰,甚至可能會關閉太陽......
請注意,具有未定義行為的單個位置會導致整個程序本身具有未定義行為!
真正理解這種東西(內存管理、指針行為等)的唯一方法是自己進行實驗。 無論如何,我聞到有人在試圖以聰明的方式愚弄學生,所以我將嘗試澄清一些事情。
int a[NUM]={20,-90,450,-37,87};
int *p;
內存中的這種結構類似於:
這創建了一個包含 5 個int
的向量,到目前為止,還不錯。 考慮到這些數據,顯而易見的舉動是使用p
遍歷 a 的元素。 您將執行以下操作:
for(p = a; p < (a + NUM); ++p) {
printf("%d ", *p);
}
但是,要注意的第一個變化是兩個循環都將指針轉換為char
。 所以,他們將是:
for (p=a;(char *)p<((char *)a+sizeof(int)*NUM); ++p) {
printf("%d ", *p);
}
代碼不是用指向int
的指針指向a
,而是將p
轉換為指向char
的指針。 假設您的機器是 32 位的。 那么int
可能會占用四個字節。 隨着p
是一個指針, int
,當你++p
,那么你有效地轉到下一個元素a
,因為透明的編譯器將跳轉四個字節。 如果將int
指針轉換為char
,則不能添加NUM
並假設您不再是數組的末尾: char
存儲在一個字節中,因此((char *)p) + 5
將指向在第二元件第二個字節a
,只要它是在開始時指向a
之前。 這是您必須調用sizeof(int)
並將其乘以NUM
,以獲得數組的末尾。
最后,臭名昭著的*p++ = ++*p<60 ? *p : 0;
*p++ = ++*p<60 ? *p : 0;
. 這對學生來說是不公平的,因為正如其他人已經指出的那樣,該代碼的行為是未定義的。 讓我們逐個表達。
++*p
意思是“訪問p
並給結果加 1。如果p
指向a
的第一個位置,那么結果就是 21。 ++*p
不僅返回 21,而且還在內存中存儲了 21你有 20 的地方。如果你只返回 21,你會寫; *p + 1
。
++*p<60 ? *p : 0
++*p<60 ? *p : 0
表示“如果p
所指向的值永久加 1 的結果小於60
,則返回該結果,否則返回 0。
*p++ = x
的意思是“存儲的值x
中的內存地址指向的p
,然后增量p
。這就是為什么你沒有找到++p
或p++
中的增量部分for
循環。
現在關於整個指令( *p++ = ++*p<60 ? *p : 0;
),它是未定義的行為(查看@Lundin 的答案以獲取更多詳細信息)。 總之,最明顯的問題是您不知道賦值運算符周圍的哪一部分(左側或右側)將首先被評估。 您甚至不知道賦值運算符右側表達式中的子表達式將如何計算(按哪個順序)。
你怎么能解決這個問題? 這實際上非常簡單:
for (p=a;(char *)p<((char *)a+sizeof(int)*NUM); ++p) {
*p = (*p + 1) <60 ? (*p + 1) : 0;
}
並且更具可讀性。 更好的是:
for (p = a; p < (a + NUM); ++p) {
*p = (*p + 1) <60 ? (*p + 1) : 0;
}
希望這可以幫助。
簡短回答:因為這條線
*p++ = ++*p<60 ? *p : 0;
無法說明程序的行為方式。 當我們訪問右側的*p
時,它是使用p
的舊值還是新值,即在左側的p++
到達它之前還是之后? C 中沒有規則可以告訴我們。 相反,有一條規則指出,由於這個原因,代碼是undefined 。
不幸的是,提出問題的人不明白這一點,認為“棘手”的代碼行這是一個令人困惑的事情,而不是不惜一切代價避免的事情。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.