C語言中「c=a+b」,這種結構合理嗎?

2020-12-16 騰訊網

C語言會同意一些"令人震驚"的結構,下面的結構是合法的嗎,我們來看看幾個例子。

c = a+++b;

以下代碼是合法的嗎,咋的一看不禁有這樣的疑問?

int a = 5, b = 7, c;

c = a+++b;

這個代碼確實不咋符合習慣的寫法,但是不管你相不相信,上面的例子是完全合乎語法的。問題是編譯器如何處理它?

根據最處理原則,編譯器應該能夠儘可能處理所有合法的用法。因此,上面的代碼會被處理成:

c = a++ + b;

我們來測試一下:

#include

int main()

{

int a = 5, b = 7, c;

c = a+++b;

printf("a = %d,b = %d,c = %d",a,b,c);

return 0;

}

輸出結果如下:

其執行順序:

b不變,c = a + b;,則c = 5 + 7 = 12

a++,那麼a = 6;

c = a+++++b;

上面那麼可能只是開胃菜,那麼這個表達式呢,有什麼想法?咱們二話不說,直接上代碼測試。

#include

int main()

{

int a = 5, b = 7, c;

c = a+++++b;

//c = (a++) + (++b);

//c = ((a++)++) + b;

printf("a = %d,b = %d,c = %d",a,b,c);

return 0;

}

編譯結果如下:

雖然一看就知道意思是(a++)+(++b);,但是編譯就通不過,我們把括號加上,c = (a++) + (++b);,編譯通過,那麼出錯信息大概是:括號影響了優先級?我們從側面以及原理來解析一下。

側面解析一下

對於a+++++b這一段代碼,編譯系統從左至右掃描整條語句,先遇到a++,判斷出來是一個a的後綴自加運算;

然後接著掃描,遇到一個+,+是一個二目運算符,它的左邊已經有一個運算數a++了,系統就向右搜索第二個運算數;

又遇到一個+,++比+的運算級別要高,這時,編譯系統就將兩個+看成一個整體來處理;

既然是++,編譯系統就認定,肯定它的左邊或右邊有一個變量,編譯系統先搜索左邊,發現++,不是變量;

再搜索右邊,發現+b,+b是什麼東西?編譯系統是無法搞明白的;

因此它就認為++是一個缺少左值的自增運算符,於是提示錯誤給用戶:lvalue required as increment operand

原理解析一下

C語言在這裡遵循詞法解析的貪婪匹配原則。優先匹配儘可能多字符的符號,無論是否有語法錯誤(因為詞法分析時還沒有語法檢查)。

於是a+++++b會被當作a ++ ++ + b,這是非法的表達式,因此產生編譯錯誤。

這個問題在ISO C99標準中直接以示例描述,原文如下:

「EXAMPLE 2 The program fragment x+++++y is parsed as x ++ ++ + y, which violates a constraint on increment operators, even though the parse x ++ + ++ y might yield a correct expression.

那麼,也許是c = ((a++)++) + b;這個意思呢?

我們編譯結果依然報錯,如下:

這涉及到「左值」的問題。百度百科定義如下:

「左值(lvalue) 是B語言/C語言/C++語言等類C語言中的一類表達式。「左」(left)的原意是指可以放在賦值符號「=」的左邊,但其實也表示能作為&和++等操作符的操作數(B語言中已經如此)。而且,現代C/C++中的含義已經不局限於此。lvalue的l被重新解釋為location。這也對應於ISO C11/ISO C++11的內存位置(memory location)。

總結:a存在左值,可以有表達式:a++。但是(a++)不存在左值,無法繼續執行(a++)++操作,所以最終報錯。

怎麼樣才能編譯正確呢?

a+++++b在編譯時會報錯,那麼a++ + ++b呢?來看以下代碼:

#include

int main()

{

int a = 5, b = 7, c;

c = a++ + ++b;

printf("a = %d,b = %d,c = %d",a,b,c);

return 0;

}

結果如下:

其代碼與c = (a++) + (++b);結果一樣,說明是正確的,其按照下面順序執行:

先執行b自加,b變為8;相當於:b = b+ 1;

求a與b之和,賦給c;相當於:c = a + b ;//c = 5+8;

執行第二步之後,a自加1:a++;

c=(++a,b++,a++,++b);

這個表達式看著爽不爽?我們知道自增自減運算,表示對自身進行a=a+1或者a=a-1的運算。

++a表示在調用前就a+1,a++表示在調用後+1。

int c=(++a,b++,a++,++b);這個逗號隔開的表示用最後一個式子對C進行賦值,測試如下:

#include

int main()

{

int a = 5, b = 7, c;

c=(++a,b++,a++,++b);

printf("a = %d,b = %d,c = %d",a,b,c);

return 0;

}

輸出的結果如下:

這段執行的順序如下

先執行++a,a=6;

再執行b++,b=8;

接下來a++,a=7;

再執行++b,b=9;

把最後一個的式子b=9的值賦給c,所以c等於9。如果改成c=(++a,b++,a++,b++);,那麼c就是等於8,因為是調用後再自增。

總結

我們看到這些有趣且不常見的代碼的時候,無需懷疑,只需要根據經驗一步一步分析,就會發現其中的玄妙之處。在此我總結以下幾點。

如果你一看就知道了答案,或猜出正確答案,說明基礎做得好,點個讚。

如果你很猶豫,不知道答案,這也是正常的,因為很少見到這樣寫代碼的,但是我們也需要去了解,才能究其根源。

我總結這個問題,是想提起一個關於代碼編寫風格,代碼的可讀性,代碼的可修改性的話題,這樣我們在寫代碼的時候才能夠注意到,更好的編碼。

相關焦點

  • C語言程序設計試題與答案B卷
    A)只能用關係表達式   B)只能用關係或邏表達式C)能用邏輯表達式    D)可以用任何表達式14、下列循環語句中( )。for (a=1,b=1;a<4&& b!
  • 三、C語言表達式、選擇結構
    第三章 表達式、選擇結構特別要注意:C語言中是用非0表示邏輯真的,用0表示邏輯假的。帶入為1<0<2,從數學的角度出發肯定是錯的,但是如果是C語言那麼就是正確的!因為要1<0為假得到0,表達式就變成了0<2那麼運算結果就是1,成為了真的了!c、等號和賦值的區別!
  • C語言基礎(下)
    C語言的世界結構體類型什麼是結構體語法定義struct Student{char * name;}stu[5];使用上與單個結構體變量相同,在數組中需要加上在數組中的下標使用示例#include <stdio.h>#include<string.h>struct Student{char
  • 用c語言編寫表白圖案_用c語言編寫一個表白
    用c語言編寫表白圖案?用c語言編寫一個表白?
  • C語言程序設計試題及答案
    A) 程序行 B) 語句 C) 函數 D) 字符2、C語言規定,在一個源程序中main函數的位置( )。A) &(*x)   B) x C) *x D) &*x13、設a,b,c都是int型變量,且a=3,b=4,c=5,則下面的表達式中,值為0的表達式是( )。
  • 已知a>b,c≠0,則下列不等式一定成立的是()A.ac>bc;B.a/c>b/c - 刀...
    題目已知a>b,c≠0,則下列不等式一定成立的是( )A.ac>bc B.a/c>b/c C.c-a>c-bD.c+a>c+b普通學生思路:不等式基本性質:(1)不等式的兩邊都加上(或減去)同一個數或同一個整式,不等號的方向不變。
  • 【C語言】函數遞歸
    遞歸作為一種算法在程序設計語言中廣泛應用。一個過程或函數在其定義或說明中有直接或間接調用自身的一種方法,它通常把一個大型複雜的問題層層轉化為一個與原問題相似的規模較小的問題來求解,遞歸策略只需少量的程序就可描述出解題過程所需要的多次重複計算,大大地減少了程序的代碼量。遞歸的能力在於用有限的語句來定義對象的無限集合。一般來說,遞歸需要有邊界條件、遞歸前進段和遞歸返回段。
  • c語言程序設計是什麼概念?及其作用與產生原因
    c語言程序設計什麼是c語言程序設計?它的作用是什麼?因為什麼而產生?首先,c語言是一門計算機高級語言,就是除了0011101這樣的二進位代碼這些基本語言外。i;for (i=0; i<5; ++i){b[i] = a[i];printf("%d\n", b[i]);}return 0;
  • c語言是C語言 C教程語言零基礎教學04:int和float變量的基本使用
    大家好,我是陌塵那麼就直接開始了我們這篇教程學for循環以及冒泡算法我們先來理解一下什麼是「循環」字面意思:但是,C語言的循環和這個有點點不同,c語言中大概就是:重複執行一段特定的代碼。且C語言裡面的循環有多種方法來實現常見的有用for和while函數我們這節課只講for函數使用方法:for(單次表達式;條件表達式;表達式){特定代碼;}好了,又到了萬眾矚目的分析時間 了(此刻應有bgm)首先 單次表達式,意思就是只會執行一次的表達式(何為表達式?百度啊!!!)
  • NJS14丨淺談C語言的運算符與表達式1
    1、算術運算符及算術表達式C語言中算術運算符有如下幾個,其中只有取正值和取負值運算符是單目運算符,其它則都是雙目運算符=123%100%10=3;  在嵌入式C語言中,數據位數分離一般用於:1)數據在屏幕上的顯示;2)數據按位數進行傳輸。
  • C語言程序設計試題1
    C.65539 D.數據不確定5.設float c,f;,將數學表達式C= (F-32)能正確表示成C語言賦值表達式的是(   ) A.c=5*(f-32)/9 B.c=5/9(f-32) C.c=5/9*(f-32) D.c=5/(9*(f-32))6.設int i=10;,表達式30-i<
  • C語言入門級教程:基礎數據類型與基本算法,學編程從此刻開始!
    ● 構造類型 在編程的過程中只使用基本類型是遠遠不夠的,為了滿足需求,因此還要使用基本類型之外的類型,構造類型就是其中的一種。 構造類型可以使用基本類型構造出新的數據類型來滿足實際開發中的需求。構造類型有3種形式:數組類型、結構體類型、共用體類型。
  • C語言項目中.h和.c文件的關係和概念
    在編譯器只認識.c(.cpp))文件,而不知道.h是何物的年代,那時的人們寫了很多的.c(.cpp)文件,漸漸地,人們發現在很多.c(.cpp)文件中的聲明語句就是相同的。
  • 學員問:C語言入門要掌握哪些基礎知識?
    01C語言程序的結構認識用一個簡單的c程序例子,介紹c語言的基本構成、格式、以及良好的書寫風格,使小夥伴對c語言有個初步認識。; /*把兩個數之和賦值給整型變量sum*/ }重點說明—1、任何一個c語言程序都必須包括以下格式:main() { }——這是c語言的基本結構,任何一個程序都必須包含這個結構。
  • C語言編寫程序輸出10個整數中最小值或最大值
    C語言編寫程序輸出10個整數中最小值或最大值這是一個比較基礎,同時也包含C語言中諸多特點的程序,仔細體會有很多的格式規則和技巧。其中包括:輸入語句,輸出語句,數組,分支結構,循環結構。希望廣大C語言學習者能夠從這個程序中感受到C程序的魅力。
  • C語言怎麼樣?今天聊聊C語言的發展史!
    這個時候的丹尼斯.裡奇也沒閒著,在對B語言改良之後,就誕生了帶有類型的C語言(據裡奇自己說,有一段時間稱這種改良的語言為NB。即:new B。不過,在我們這些吃瓜群眾眼中看來也確實NB)。 4. unix時間線
  • 童裝a類b類c類是什麼意思 童裝c類能穿嗎
    童裝a類b類c類是什麼意思 童裝c類能穿嗎 2020-04-07 15:35:31 來源:全球紡織網 童裝a類b類c類是什麼意思?童裝c類能穿嗎?
  • 【C/C+】10個經典的C語言小程序,小白必看!
    n"); printf(" ****\n"); printf(" *\n"); printf(" * \n"); printf(" ****\n"); } 7、題目:輸出特殊圖案,請在c環境中運行
  • C語言宏定義#define的理解與資料整理
    但是我們利用define來定義數值類型的數據,一般只是用來定義  常量 ,如果 要定義一些變量,則可以使用c語言中const這個關鍵字。我們已經討論了const 這個關鍵字,我們知道const 修飾的數據是有類型的,而define 宏定義的數據沒有類型。
  • C語言入門經典:必背18個經典程序
    */main(){ int  a[4][4],b[4][4],i,j;       /*a存放原始數組數據,b存放旋轉後數組數據*/  printf("input 16 numbers: ");/*輸入一組數據存放到數組a中,然後旋轉存放到b數組中*/  for(i=0;i<4;i++)      for(j=0;j&