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

2020-12-18 騰訊網

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語言結構體成員大小的方法,萌新們掌握了嗎
    在C語言編程中,有時候需要知道某結構體中某成員的大小,比如使用堆內存來存儲結構體中的某成員時,需要知道該成員的大小,才好確定所需申請的空間大小。求某結構體中某成員的大小,你會怎麼做?這種方法較方法一的好處就是不用定義一個多餘的變量。
  • C語言程序設計試題與答案B卷
    A)只能用關係表達式   B)只能用關係或邏表達式C)能用邏輯表達式    D)可以用任何表達式14、下列循環語句中( )。for (a=1,b=1;a<4&& b!
  • 三、C語言表達式、選擇結構
    第三章 表達式、選擇結構特別要注意:C語言中是用非0表示邏輯真的,用0表示邏輯假的。C語言有構造類型,沒有邏輯類型。關係運算符號:注意<=的寫法,==和=的區別!(考試重點)if只管後面一個語句,要管多個,請用大括號!
  • 「C語言」這種求結構體成員大小的方法,你可能需要了解一下
    在C語言編程中,有時候需要知道某結構體中某成員的大小,比如使用堆內存來存儲結構體中的某成員時,需要知道該成員的大小,才好確定所需申請的空間大小。求某結構體中某成員的大小,你會怎麼做?例子:typedef struct{    char a;     char c;    short b;           int d;&
  • C語言中的運算符
    一、前言在嵌入式開發中,我們一般選用C語言作為開發語言。一個主要的原因在於C語言提供了大量的運算符,比如我們對硬體的操作一般涉及到對位的操作,而C語言就提供了這樣的運算符,這是其他語言不具備的。二、C語言運算符有哪些C語言有大量的運算符,可大致分為以下幾類:括號與結構體符號、單目、算術、移位、關係、位、邏輯、三目、賦值、逗號。
  • 為什麼本周震蕩盤整的結構是a-b-c
    來源:風雲在線66行情來了沒有開戶三分鐘快速開戶指引》》》》為什麼本周震蕩盤整的結構是a-b-c(說說數浪)昨天又朋友問了,紋身嗎我的觀點是a-b-c,大家注意這個a-b-c是小寫,小寫和大些的意義不一樣,這是第一點。
  • C語言-數據結構
    數據結構介紹程序(Program)=數據結構(Data Structure)+算法(Algorithm)數據結構是計算機專業中一門綜合性的基礎課程,它是介於數學,計算機硬體和計算機軟體的三者之間一門核心課程,同時,數據結構是設計資料庫,程序,作業系統,遊戲等等設計方面的重要基礎
  • C語言學習篇(21)----結構體初探
    結構體的定義使用struct關鍵字定義原生結構體類型struct people{ char name[20]; int age;};使用typedef類型自定義結構體類型>編譯運行:結構體的元素訪問在C語言中有兩種方式訪問
  • C語言全局變量那些事兒
    (感謝網友 @我的上鋪叫路遙 投稿)作為一名程式設計師,如果說沉迷一門程式語言算作一種樂趣的話,那麼與此同時反過來去黑一門程式語言就是這種樂趣的升華。但令人驚奇的是無論是在main.c還是foo.c中,a和b都是相同的地址,也就是說,a和b被定義了兩次,b還是不同類型,但內存映像中只有一份拷貝。我們還看到,main.c中b的值居然就是foo.c中結構體第一個成員變量b.a的值,這證實了前面的推斷——即便存在多次定義,內存中只有一份初始化的拷貝。另外在這裡c是置身事外的一個獨立變量。為何會這樣呢?
  • C/C+編程筆記:C語言預處理命令是什麼?不要以為你直接寫#就行!
    其實這種以#號開頭的命令稱為預處理命令。 關於編譯和連結的過程、目標文件和可執行文件的結構、.h 文件和 .c 文件的區別,我們將在後期專題中講解。 在實際開發中,有時候在編譯之前還需要對源文件進行簡單的處理。
  • 單片機中C語言延時函數
    單片機C語言延時程序計算2009-11-02 22:15單片機C語言延時程序用C語言寫出來程序非常的簡練,它是一種模塊化的語言,一種比彙編更高級的語言,但是就是這樣一種語言也還是有它不足之處:它的延時很不好控制
  • 單片機C語言教程-基礎語句
    C語言入門之基礎語句  從程序流程的角度來看,程序可以分為三種基本結構,即順序結構、分支結構、循環結構。這三種基本結構可以組成所有的各種複雜程序。c語言提供了多種語句來實現這些程序結構。
  • 深入理解C語言
    導讀:Dennis Ritchie過世了,他發明了C語言,一個影響深遠並徹底改變世界的計算機語言。一門經歷40多年的到今天還長盛不訓的語言,今天很多語言都受到C的影響,C++,Java,C#,Perl,PHP,Javascript等等。但是,你對C了解嗎?相信你看過本站的《C語言的謎題》還有《誰說C語言很簡單?》。
  • C語言深入淺出 :回味經典
    【IT168 資訊】Dennis Ritchie 過世了,他發明了C語言,一個影響深遠並徹底改變世界的計算機語言。一門經歷40多年的到今天還長盛不衰的語言,今天很多語言都受到C的影響,C++,Java,C#,Perl, PHP, Javascript, 等等。但是,你對C了解嗎?
  • C語言學習篇(32)——為什麼C語言不能函數重載
    在C++中原生支持了函數重載, 而在C語言中並不支持,只能通過一些技巧來變相解決, 如定義flag形參, 根據flag值不同,進行不同的處理。*)a; char *m_b = (char *)b; } else if(flag == 3) { int *m_a = (int *)a; char *m_b = (char *)b; }}而在C++中,直接函數重載://===========
  • C語言基礎(下)
    C語言的世界結構體類型什麼是結構體語法定義struct Student{char * name;}stu[5];使用上與單個結構體變量相同,在數組中需要加上在數組中的下標使用示例#include <stdio.h>#include<string.h>struct Student{char
  • 單片機C語言中的空語句
    標準的C語言中沒有空語句。但在單片機的C語言編程中,經常需要用幾個空指令產生短延時的效果。這在彙編語言中很容易實現,寫幾個nop就行了。
  • 「C語言從入門到入土」必備C語言基礎筆記整理
    一、C語言1、什麼是C語言?C語言是人寫機器看的一種語言。C語言是高級語言中的低級語言。C語言貼近硬體。C語言的入門學習比較簡單。彙編語言——>B語言——>C語言2、C語言的特性首先C語言就是你的女朋友。
  • 自學單片機第八篇:基礎知識——C語言基礎
    大學C語言複習重點1)程序結構是三種:順序結構 、選擇結構(分支結構)、循環結構。2)讀程序都要從main()入口, 然後從最上面順序往下讀(碰到循環做循環,碰到選擇做選擇),有且只有一個main函數。3)計算機的數據在電腦中保存是以 二進位的形式。數據存放的位置就是 他的地址。4)bit是位 是指為0 或者1。
  • 為什麼學c語言及其計算機語言的原理
    而其技能當然不僅僅是做表或是寫文檔怎麼簡單,而我們就很難以外行人身份去研究計算機硬體及其工作原理,所以學會一門計算機程式語言就成了我們掌握計算機技能的突破口。既可以相對了解硬體的基本情況,也清楚軟體系統在計算機內部運作的過程。計算機語言的發展,是從機器語言、彙編語言、高級語言。而機器語言也就是計算機運行的原理。