[C/C++]一個比較詭異的"UB問題"——*p++和*(p++)

首先看這段代碼:

#include <stdio.h>

int main()
{
   int a[] = {0, 2, 4, 6, 8};
   int *p = a;
   // 打印頭兩位的內存地址
   printf("\n%d %d", p, p+1 ); // 1143884624 1143884628
   // 此處兩個操作都返回的是*p的值(即a[0]),沒有修改過p的指向
   printf("\n%d %d", *(p++), *p ); // 0 0
   // 再次打印*p,發現p的指向變了
   printf("\n%d", *p );	 // 2
   // 查看地址,果然和第一行中p+1的內存地址相同
   printf("\n%d", p ); // 1143884628
   // 最後,作爲對比查看原數組,並沒有任何值發生改變
   printf("\n%d %d %d", a[0], a[1], a[3]); // 0 2 4
   return 0;
}

看起來是個比較詭異且反直覺的UB問題,但其實不是


要解釋這個問題,首先是要說明“前後自增”的彙編方式:

觀察下面的代碼:

#include <stdio.h>

int main()
{
   int a = 1;
   int b = a++;
   printf("%d %d", a, b); // 很顯然會打印 2 1
   // 如果改成++a 會打印 2 2
   // 根據算符的結合順序,改成 b = (a++),結果也是 2 1
   return 0;
}

從彙編的角度出發解釋這個反直覺現象:

int b = a ++

  1. a的值被取出,放到eax;
  2. eax+1放到edx;
  3. a=edx
  4. b=eax
  • 1~4步即:ba原值,a值加一。

int b = ++a

  1. a=a+1;
  2. a的值被取出,放到eax;
  3. b=eax
  • 1~3步即:a值加一後b再取a的值。

其次,++*都是右結合,所以從結合的角度來說,由於++優先級大於*,即*p++*(p++)二者必然等價。如果你能理解 int d = *p++取的是a[0]的值,那麼根據等價關係,實際上*(p++)也是取到a[0],而:

printf("\n%d %d", *(p++), *p ); // 0 0

之所以打印了兩次a[0],在printf中,同一地址只取值一次,執行順序並不是括號內從左到右的,因此本行中p始終指向a[0]的地址,但是下一行再次打印*p,由於*(p++)(或*p++)的存在,此時應該打印p[1]的值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章