簡體   English   中英

C語言:查找給定程序的輸出

[英]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的值
  • 如果結果小於 60,則使用它,否則使用 0
  • 將此分配給*p
  • 增加p

但是,這是未定義的行為,因為在p讀寫訪問之間沒有序列點; 你不知道是先評估左邊還是右邊,在前一種情況下,我們會分配a[i] = ++a[i + 1] ,在后一種情況下, a[i] = ++a[i] 你可能用另一個編譯器得到了不同的輸出!!!

然而,這只是兩個最有可能的輸出——實際上,如果陷入未定義的行為,任何事情都可能發生,編譯器可能只是忽略有問題的代碼片段,決定什么都不做(只是從主右退出第一條指令),程序可能會崩潰,甚至可能會關閉太陽......

請注意,具有未定義行為的單個位置會導致整個程序本身具有未定義行為!

真正理解這種東西(內存管理、指針行為等)的唯一方法是自己進行實驗。 無論如何,我聞到有人在試圖以聰明的方式愚弄學生,所以我將嘗試澄清一些事情。

int a[NUM]={20,-90,450,-37,87};
int *p;

內存中的這種結構類似於:

一個指針 <code>p</code> 指向 <code>a</code> 中的第一個元素。

這創建了一個包含 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 。這就是為什么你沒有找到++pp++中的增量部分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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM