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

2020-12-24 騰訊網

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語言的世界結構體類型什麼是結構體我們觀察現實世界的時候,可以發現很多的東西它都是由不同的部分組合起來的語法定義struct Student{char * name;}stu[5];使用上與單個結構體變量相同,在數組中需要加上在數組中的下標使用示例#include <stdio.h>#include<string.h>struct Student{char
  • (精華)C語言為什麼一般不在.h中定義函數或者變量?
    大部分C編程愛好者都知道,在我們的.h文件裡面經常看到的是函數的聲明、變量的聲明、以及各種各樣的宏等等,而且在我前面的文章中我也提到過C語言的模塊化設計中常常說到對應的.h和.c文件認為是一個對象,那麼.h文件主要是對外的一些接口
  • C語言入門級教程:基礎數據類型與基本算法,學編程從此刻開始!
    ● 構造類型 在編程的過程中只使用基本類型是遠遠不夠的,為了滿足需求,因此還要使用基本類型之外的類型,構造類型就是其中的一種。 構造類型可以使用基本類型構造出新的數據類型來滿足實際開發中的需求。構造類型有3種形式:數組類型、結構體類型、共用體類型。
  • 學員問:C語言入門要掌握哪些基礎知識?
    01C語言程序的結構認識用一個簡單的c程序例子,介紹c語言的基本構成、格式、以及良好的書寫風格,使小夥伴對c語言有個初步認識。; /*把兩個數之和賦值給整型變量sum*/ }重點說明—1、任何一個c語言程序都必須包括以下格式:main() { }——這是c語言的基本結構,任何一個程序都必須包含這個結構。
  • 初中數學 二次函數y=ax+bx+c(a≠0)中係數a,b,c的幾何意義 - 周...
    在二次函數的學習中,經常會碰到有一類題型,需要判斷函數係數的關係式是否成立,好多同學總是一頭霧水,不知從何下手。老師今天分享這方面的知識,掌握了一定會解決你的困惑。那麼在解這種題目時,需要弄清楚係數a,b,c與二次函數的關係,以及拋物線在直角坐標系中的幾何意義。
  • C語言---判斷三角形的類型
    #include <stdio.h>#include <math.h>int main(){    float a,b,c;//定義三個變量儲存三角形的三條邊    float s,mj;//儲存三角形的面積    printf("請輸入三角形的三條邊:\n");    scanf("%f,%f,%f",
  • C語言宏定義#define的理解與資料整理
    但是我們利用define來定義數值類型的數據,一般只是用來定義  常量 ,如果 要定義一些變量,則可以使用c語言中const這個關鍵字。我們已經討論了const 這個關鍵字,我們知道const 修飾的數據是有類型的,而define 宏定義的數據沒有類型。
  • keil C語言與彙編語言混合編程
    同時注意,為了能夠讓彙編語言 能訪問到C語言中定義的變量和函數,他們必須聲明為外部變量,即加extern 前綴。(2) c程序中訪問彙編程序中的變量如果需要在c程序中訪問彙編程序中的變量,則彙編程序中的變量名必須以下劃線為首字符,並用global使之成為全局變量。
  • 單片機的程式語言:彙編語言、C語言、PL/M和BASIC語言
    這種模塊化的程序設計增加了程序設計的靈活性和方便性。3.可靠性高PL/M一51編譯軟體檢查用戶程序對單片機的各種資源使用情況,對衝突使用情況或不合理使用情況能提出警告,並能自動為用戶程序合理分配內存,避免計算機中有效資源的浪費,為使用者提供方便。
  • 單片機C語言基礎專題05 - 邏輯運算
    報名連結:單片機C語言基礎專題課程 - 報名即送STC8G單片機基礎仿真板基礎理論:邏輯與或非,運算對象是布爾值(1或0,真或假),類似於數字電路的與門,或門,非門。與關係運算符配合,一般用於選擇語句與循環語句中1、邏輯與符號為&&。
  • 編程題 | 4 C選擇結構程序設計
    scanf("%f,%f,%f",&a,&b , &c);  if(a+b>c && b+c>a && a+c>b)   {     s=(a+b+c)/2;    area=sqrt(s*(s*(s-a)*(s-b)*(s-c)));    printf
  • 入門C語言中的數組,字符串常量與指針
    MAX_SIZE + 1];C數組開始於元素0,所以像陣列定義 int a[3];將創建三個int元件,可尋址如a[0],a[1],和a[2]請注意,即使定義很難說a[3],也沒有名為a[3]與其他變量一樣,全局和靜態數組元素默認情況下初始化為0,並且自動數組元素填充有垃圾值可以使用大括號中的一個或多個值來初始化數組
  • 單片機c語言教程:建立你的第一個KeilC51項目
    KEIL uVISION2 是眾多單片機應用開發軟體中優秀的軟體之一,它支持眾多不一樣公司的MCS51架構的晶片,它集編輯,編譯,仿真等於一體,同時還支持,PLM,彙編和C語言的程序設計,它的界面和常用的微軟 VC++的界面相似,界面友好,易學易用,在調試程序,軟體仿真方面也有很強大的功能。本站提供的單片機c語言教程都是基於keilc51的。
  • 單片機C語言模塊化編程方法
    模塊化的好處是很多的,不僅僅是便於分工,它還有助於程序的調試,有利於程序結構的劃分,還能增加程序的可讀性和可移植性。初學者往往搞不懂如何模塊化編程,其實它是簡單易學,而且又是組織良好程序結構行之有效的方法之一.本文將先大概講一下模塊化的方法和注意事項,最後將以初學者使用最廣的keil c編譯器為例,給出模塊化編程的詳細步驟。
  • 如果是初學C語言請看完,如何學好C語言絕對精品
    自然,我讓你輸入程序不是讓你練習盲打,你在輸入的過程就可以品出程序的好壞來了,如果發現自己輸入了很多重複代碼,那至少這個程序的結構就不好了,窮則變,變則通嘛,你就想改進它啊,這不是很好嗎?當然,程序輸得多了,盲打功力也上了一個臺階,鍵盤敲得快了,薪水也跟著漲上去了!
  • 如何辨別a、b、c類翡翠?教您幾招讓您購買放心!
    在講述我們的方法之前,先簡單說明一下a、b、c類翡翠究竟是個什麼樣的概念,這樣會方便您在購買前有個大致的了解。a貨天然生成的翡翠,只用了物理方法進行加工雕刻打磨和拋光,沒有經歷任何化學手法對其進行加工處理。
  • C/C+編程筆記:在C+中如何調用C語言的代碼?你可以這樣做
    比如在C文件中存在一個函數func_c(), 該文件與C++的工程混編在一起時,可以直接在C++中調用C文件中的func_c();不需要做任何額外處理。 不過在C中,不可以調用C++的接口,也不能直接調用C語言的一些API,否則編譯會出錯。這個時候呢,就需要我們今天的主角:extern "C"了!
  • 越南歌曲:Bật Nhạc Lên
    Khi em khóc hãy bật nhạc lênTừng câu hát sẽ mang đi niềm đauCó những nổi nhớ ta phải quênĐể em bước tiếp những ngày sauu x2Nghe bài hát cũng có thể đoán tâm trạng của em
  • 怎樣學習C語言(獻給迷茫的C愛好者)!
    自然,我讓你輸入程序不是讓你練習盲打,你在輸入的過程就能夠品出程序的好壞來了,假設發現自己輸入了非常多反覆代碼,那至少這個程序的結構就不好了,窮則變,變則通嘛,你就想改進它啊,這不是非常好嗎?    當然,程序輸得多了,盲打功力也上了一個臺階,鍵盤敲得快了,薪水也跟著漲上去了!
  • C語言中的變量存儲類型static老手都這樣用
    請看正文C語言中的變量有哪些存儲類型,你還記得嗎?static老手都這樣用。1、 先來回顧C語言變量C語言中變量值的存儲位置有兩類:CPU的寄存器和內存。變量存儲類型關係到其存儲位置,除了register型存儲在CPU寄存器中,C語言提供的其它三種存儲類型(auto型、static型、extern型)的變量均存儲在內存中。