<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Linux內核中C語法擴展-語句表達式

汽車電子技術 ? 來源:宅學部落 ? 作者:王利濤 ? 2023-02-17 09:30 ? 次閱讀

基礎復習:表達式、語句和代碼塊

表達式

表達式和語句是 C 語言中的基礎概念。什么是表達式呢?表達式就是由一系列操作符和操作數構成的式子。操作符可以是 C 語言標準規定的各種算術運算符、邏輯運算符、賦值運算符、比較運算符等。操作數可以是一個常量,也可以是一個變量。表達式也可以沒有操作符,單獨的一個常量甚至是一個字符串,也是一個表達式。下面的字符序列都是表達式:

  • 2 + 3
  • 2
  • i = 2 + 3
  • i = i++ + 3
  • "wit"

表達式一般用來數據計算或實現某種功能的算法。表達式有2個基本屬性:值和類型。如上面的表達式2+3,它的值為5。根據操作符的不同,表達式可以分為多種類型,如:

  • 關系表達式
  • 邏輯表達式
  • 條件表達式
  • 賦值表達式
  • 算術表達式
  • ……

語句

語句是構成程序的基本單元,一般形式如下:

表達式 ;
i = 2 + 3 ;

表達式的后面加一個; 就構成了一條基本的語句。編譯器在編譯程序、解析程序時,不是根據物理行,而是根據分號 ; 來判斷一條語句的結束標記的。如 i = 2 + 3; 這條語句,你寫成下面的形式也是可以編譯通過的:

i =
2 + 
3
;

代碼塊

不同的語句,使用大括號{}括起來,就構成了一個代碼塊。C 語言允許在代碼塊里定義一個變量,這個變量的作用域也僅限于這個代碼塊內,因為編譯器就是根據{}來做入棧出棧操作來管理變量的作用域的。如下面的程序:

int main(void)
{
    int i = 3;
    printf("i=%d
",i);
    {
        int i = 4;
        printf("i=%d
",i);
    }
    printf("i=%d
",i);
    return 0;
}
運行結果為:
i=3i=4i=3

語句表達式

語句表達式的定義

GNU C 對 C 標準作了擴展,允許在一個表達式里內嵌語句,允許在表達式內部使用局部變量、for 循環和 goto 跳轉語句。這樣的表達式,我們稱之為語句表達式。語句表達式的格式如下:

({ 表達式1; 表達式2; 表達式3; })

語句表達式最外面使用小括號()括起來,里面一對大括號{}包起來的是代碼塊,代碼塊里允許內嵌各種語句。語句的格式可以是 “表達式;”這種一般格式的語句,也可以是循環、跳轉等語句。

跟一般表達式一樣,語句表達式也有自己的值。語句表達式的值為內嵌語句中最后一個表達式的值。我們舉個例子,使用語句表達式求值。

int main(void)
{
  int sum = 0;
  sum =
  ({
    int s = 0;
    for( int i = 0; i < 10; i++)
      s = s + i;
      s;
  });
  printf("sum = %d
",sum);
  return 0;
}

在上面的程序中,通過語句表達式實現了從1到10的累加求和,因為語句表達式的值等于最后一個表達式的值,所以在 for 循環的后面,我們要添加一個 s; 語句表示整個語句表達式的值。如果不加這一句,你會發現 sum=0?;蛘吣銓⑦@一行語句改為100; 你會發現最后 sum 的值就變成了100,這是因為語句表達式的值總等于最后一個表達式的值。

語句表達式內使用 goto 跳轉

在上面的程序中,我們在語句表達式內定義了局部變量,使用了 for 循環語句。在語句表達式內,我們同樣也可以使用 goto 進行跳轉。

int main(void)
{
  int sum = 0;
  sum = ({
    int s = 0;     
    for( int i = 0; i < 10; i++)
       s = s + i;        
       goto here;
       s;  
    });
  printf("sum = %d
",sum);
  here:
    printf("here:
");
    printf("sum = %d
",sum);
  return 0;
}

在宏定義中使用語句表達式

語句表達式的亮點在于定義復雜功能的宏。使用語句表達式來定義宏,不僅可以實現復雜的功能,而且還能避免宏定義帶來的歧義和漏洞。下面就以一個宏定義例子,讓我們來見識見識語句表達式在宏定義中的強悍殺傷力!

假如你此刻正在面試,面試職位是:Linux C 語言開發工程師。面試官給你出了一道題:

請定義一個宏,求兩個數的最大值。

別看這么簡單的一個考題,面試官就能根據你寫出的宏,來判斷你的 C 語言功底,來決定給不給你 Offer。

合格

對于學過 C 語言的同學,寫出這個宏基本上不是什么難事,使用條件運算符就能完成:

#define  MAX(x,y)  x > y ? x : y

這是最基本的 C 語言語法,如果連這個也寫不出來,估計場面會比較尷尬。面試官為了緩解尷尬,一般會對你說:小伙子,你很棒,回去等消息吧,有消息,我們會通知你!這時候,你應該明白:不用再等了,趕緊把這篇文章看完,接著面下家。這個宏能寫出來,也不要覺得你很牛 X,因為這只能說明你有了 C 語言的基礎,但還有很大的進步空間。比如,我們寫一個程序,驗證一下我們定義的宏是否正確:

#define MAX(x,y) x > y ? x : y
int main(void)
{
  printf("max=%d",MAX(1,2));
  printf("max=%d",MAX(2,1));
  printf("max=%d",MAX(2,2));
  printf("max=%d",MAX(1!=1,1!=2));
  return 0;
}

測試程序嗎,我們肯定要把各種可能出現的情況都測一遍。這不,測試第4行語句,當宏的參數是一個表達式,發現實際運行結果為 max=0,跟我們預期結果 max=1 不一樣。這是因為,宏展開后,就變成了這個樣子:

printf("max=%d",1!=1>1!=2?1!=1:1!=2);

因為比較運算符 > 的優先級為6,大于 !=(優先級為7),所以展開的表達式,運算順序發生了改變,結果就跟我們的預期不一樣了。為了避免這種展開錯誤,我們可以給宏的參數加一個小括號()來防止展開后,表達式的運算順序發生變化。這樣的宏才能算一個合格的宏:

#define MAX(x,y) (x) > (y) ? (x) : (y)

中等

上面的宏,只能算合格,但還是存在漏洞。比如,我們使用下面的代碼測試:

#define MAX(x,y) (x) > (y) ? (x) : (y)
int main(void)
{
  printf("max=%d",3 + MAX(1,2));
  return 0;
}

在程序中,我們打印表達式 3 + MAX(1, 2) 的值,預期結果應該是5,但實際運行結果卻是1。我們展開后,發現同樣有問題:

3 + (1) > (2) ? (1) : (2);

因為運算符 + 的優先級大于比較運算符 >,所以這個表達式就變為4>2?1:2,最后結果為1也就見怪不怪了。此時我們應該繼續修改這個宏:

#define MAX(x,y) ((x) > (y) ? (x) : (y))

使用小括號將宏定義包起來,這樣就避免了當一個表達式同時含有宏定義和其它高優先級運算符時,破壞整個表達式的運算順序。如果你能寫到這一步,說明你比前面那個面試合格的同學強,前面那個同學已經回去等消息了,我們接著面試下一輪。

良好

上面的宏,雖然解決了運算符優先級帶來的問題,但是仍存在一定的漏洞。比如,我們使用下面的測試程序來測試我們定義的宏:

#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void)
{    
  int i = 2;
  int j = 6;
  printf("max=%d",  MAX(i++,j++));
  return 0;
}

在程序中,我們定義兩個變量 i 和 j,然后比較兩個變量的大小,并作自增運算。實際運行結果發現 max = 7,而不是預期結果 max = 6。這是因為變量 i 和 j 在宏展開后,做了兩次自增運算,導致打印出 i 的值為7。

遇到這種情況,那該怎么辦呢? 這時候,語句表達式就該上場了。我們可以使用語句表達式來定義這個宏,在語句表達式中定義兩個臨時變量,分別來暫儲 i 和 j 的值,然后進行比較,這樣就避免了兩次自增、自減問題。

#define MAX(x,y)({  
    int _x = x;       int _y = y;       _x > _y ? _x : _y; })
int main(void)
{
    int i = 2;
    int j = 6;
    printf("max=%d",    MAX(i++,j++));  
    return 0;
}

在語句表達式中,我們定義了2個局部變量 _x、_y 來存儲宏參數 x 和 y 的值,然后使用 _x 和 _y 來比較大小,這樣就避免了 i 和 j 帶來的2次自增運算問題。

你能堅持到了這一關,并寫出這樣自帶 BGM 的宏,面試官心里可能已經有了給你 Offer 的意愿了。但此時此刻,千萬不要驕傲!為了徹底打消面試官的心理顧慮,我們需要對這個宏繼續優化。

優秀

在上面這個宏中,我們定義的兩個臨時變量數據類型是 int 型,只能比較兩個整型的數據。那對于其它類型的數據,就需要重新再定義一個宏了,這樣太麻煩了!我們可以基于上面的宏繼續修改,讓它可以支持任意類型的數據比較大?。?/p>

#define MAX(type,x,y)({  
    type _x = x;      type _y = y;      _x > _y ? _x : _y; })

int main(void)
{
    int i = 2;
    int j = 6;
    printf("max=%d
",    MAX(int,i++,j++));
    printf("max=%f
",    MAX(float,3.14,3.15));
    return 0;
}

在這個宏中,我們添加一個參數:type,用來指定臨時變量 _x 和 _y 的類型。這樣,我們在比較兩個數的大小時,只要將2個數據的類型作為參數傳給宏,就可以比較任意類型的數據了。如果你能在面試中,寫出這樣的宏,面試官肯定會非常高興,他一般會跟你說:稍等,待會 HR 會跟你談待遇問題。恭喜你,你拿到 Offer 了!

語句表達式在 Linux 內核中的使用

語句表達式,作為 GNU C 對 C 標準的一個擴展,在內核中,尤其是在內核的宏定義中,被大量的使用。使用語句表達式定義宏,不僅可以實現復雜的功能,還可以避免宏定義帶來的一些歧義和漏洞。比如在 Linux 內核中,max_tmin_t 的宏定義,就使用了語句表達式:

#define min_t(type, x, y) ({
    type __min1 = (x); 
    type __min2 = (y); 
    __min1 < __min2 ? __min1 : __min2; })
#define max_t(type, x, y) ({ 
    type __max1 = (x); 
    type __max2 = (y); 
    __max1 > __max2 ? __max1 : __max2; })

除此之外,在 Linux 內核、GNU 開源軟件中,你會發現,還有大量的宏定義使用了語句表達式。通過本節教程的學習,相信大家以后再碰到這種使用語句表達式定義的宏,肯定就知道是怎么回事了,心中有丘壑,再也不用慌。

備注

本教程是《C語言嵌入式Linux高級編程》第5期:Linux內核中的GNU C語法擴展,文本預覽版,如果想系統學習Linux內核中的各種GNU C擴展及使用技巧,可百度搜索:“王利濤”,到51CTO學院或CSDN學院點擊相關課程即可開始系統學習。

如果您手頭暫時沒有 Linux 學習環境,也可以在 Windows 環境下安裝 C-Free 學習。教程中的 C 語言示例程序在 C-Free 環境下面也能編譯通過。當然在這里,還是建議您使用虛擬機安裝一個 Linux 學習環境,一個良好的環境更有利于我們的學習,在安裝過程有什么疑惑,可以通過郵件(3284757626@qq.com)聯系,也可以加入QQ群(475504428),參與技術討論。

微信公眾號:宅學部落

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11022

    瀏覽量

    207059
  • C語言
    +關注

    關注

    180

    文章

    7548

    瀏覽量

    131345
  • LINUX內核
    +關注

    關注

    1

    文章

    312

    瀏覽量

    21419
  • 運算符
    +關注

    關注

    0

    文章

    163

    瀏覽量

    10962
  • 操作符
    +關注

    關注

    0

    文章

    20

    瀏覽量

    9009
收藏 人收藏

    評論

    相關推薦

    什么是正則表達式?正則表達式如何工作?哪些語法規則適用正則表達式?

    正則表達式又稱規則表達式(Regular Expression,在代碼中常簡寫為 regex、regexp 或 RE),是一種用于匹配、查找、替換文本的強大工具。它能夠以特定的模式匹配字符串,從而
    的頭像 發表于 11-03 14:41 ?635次閱讀
    什么是正則<b class='flag-5'>表達式</b>?正則<b class='flag-5'>表達式</b>如何工作?哪些<b class='flag-5'>語法</b>規則適用正則<b class='flag-5'>表達式</b>?

    MATLAB符號表達式

    符串并說明diff( ' cosx ' )是一個符號表達式而不是數字表達式;然而在第二個例子,用函數sym顯式地告訴MATLAB M=sym( ' [a,b;c,d] ' )是一符號
    發表于 09-22 15:45

    【跟我學單片機C語言】全面分析C51表達式語句及仿真器

    在前面的課程的例子里,也簡單理解過一些語句的使用方法,能看出C語言是一種結構化的程序設計語言。C 語言供給了相當豐富的程序控制語句。學習掌
    發表于 12-12 10:27

    C語言:指針表達式

    字符'b' printf("%c\n",*cp);//還是字符'a' return 0;}*(cp+1)在前面的表達式增加了一個括號。這個括號使表達式先執行加法運算,就是把1和cp中
    發表于 01-11 13:41

    關于C語言的運算符和表達式--筆記1

    十六進制的10賦值變量a;f= d-e;//將d-e的值賦于變量f; 賦值語句的意義就是先計算出“=”右邊的表達式的值,然后將得到的值賦給左邊的變量。 2:算術運算A:算術運算符和算術表達式
    發表于 07-19 00:48

    總結linux腳本語法和正則表達式的應用

    每日學一點之linux腳本語法以及正則表達式基礎
    發表于 11-08 09:23

    【LabVIEW懶人系列教程-小白入門】1.7LabVIEW數據操作之表達式

    ③利用公式節點需要注意的是,公式節點的語法類似c語言,但是有差異,務必參考幫助語法使用。驗證正確今日總結①如何利用數學函數編寫表達式
    發表于 07-29 00:48

    Linux內核C語言語法擴展

    收集一些感覺比較好的博客鏈接,方便以后再看Linux 內核C 語言語法擴展
    發表于 12-15 06:04

    防范表達式的失控

    在C 語言中,表達式是最重要的組成部分之一,幾乎所有的代碼都由表達式構成。表達式的使用如此廣泛,讀者也許會產生這樣的疑問,像+ 、- 、3 、/ 、& & 這樣簡單的運算也會出現
    發表于 04-22 16:57 ?13次下載

    第十課 C51表達式語句及仿真器

    豐富的程序控制語句。學習掌握這些語句的使用方法也是單片機C語言學習中的重點。 表達式語句是最基本的一種語句。不一樣的程序設計語言都會有不一樣
    發表于 11-22 12:45 ?485次閱讀
    第十課 C51<b class='flag-5'>表達式</b><b class='flag-5'>語句</b>及仿真器

    C語言的表達式

    在C語言中,表達式是由操作符和操作數組成。表達式可以由一個或者多個操作數組成,不同的操作符與操作數組成不同的表達式,因此,表達式才是C語言的基本。
    的頭像 發表于 02-21 15:09 ?1012次閱讀
    C語言的<b class='flag-5'>表達式</b>

    Linux入門之正則表達式

    正則表達式是用來表達字符串匹配模式的方法,利用正則表達式,可以讓我們輕易地實現對目標字符串的**查找**、**刪除**、**替換**等操作。 正則表達式并不復雜,它并不包含難以理
    的頭像 發表于 05-12 15:31 ?605次閱讀
    <b class='flag-5'>Linux</b>入門之正則<b class='flag-5'>表達式</b>

    shell腳本基礎:正則表達式grep

    Linux上有許多命令可以使用正則表達式,其中最常見的是grep命令和sed命令。正則表達式有多種類型,每種類型中可以使用的元字符類型不同。最常見的是基本正則表達式
    的頭像 發表于 05-29 09:29 ?812次閱讀
    shell腳本基礎:正則<b class='flag-5'>表達式</b>grep

    zabbix觸發器表達式 基本RS觸發器表達式 rs觸發器的邏輯表達式

    zabbix觸發器表達式 基本RS觸發器表達式 rs觸發器的邏輯表達式? Zabbix是一款開源的監控軟件,它能通過監控指標來實時監測服務器和網絡的運行狀態,同時還能提供警報和報告等功能來幫助管理員
    的頭像 發表于 08-24 15:50 ?1286次閱讀

    linux正則表達式匹配字符串

    Linux操作系統中,正則表達式是一種強大的模式匹配工具,它可以用于在文本中查找、替換和篩選特定的字符串。準確掌握正則表達式語法和常見應用,對于
    的頭像 發表于 11-23 10:26 ?388次閱讀
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>