【嵌入式】C语言高级编程-语句表达式(03)
00. 目錄
文章目錄
- 00. 目錄
- 01. C語言的表達(dá)式
- 02. C語言的語句
- 03. C語言中的代碼塊
- 04. C語言中的語句表達(dá)式
- 05. 宏中使用語句表達(dá)式
- 06. Linux內(nèi)核應(yīng)用示例
- 07. 附錄
01. C語言的表達(dá)式
表達(dá)式和語句是 C 語言中的基礎(chǔ)概念。什么是表達(dá)式呢?表達(dá)式就是由一系列操作符和操作數(shù)構(gòu)成的式子。操作符可以是 C 語言標(biāo)準(zhǔn)規(guī)定的各種算術(shù)運(yùn)算符、邏輯運(yùn)算符、賦值運(yùn)算符、比較運(yùn)算符等。操作數(shù)可以是一個(gè)常量,也可以是一個(gè)變量。表達(dá)式也可以沒有操作符,單獨(dú)的一個(gè)常量甚至是一個(gè)字符串,也是一個(gè)表達(dá)式。下面的字符序列都是表達(dá)式:
22 + 3322a = a + bi++"hello world"表達(dá)式一般用來數(shù)據(jù)計(jì)算或?qū)崿F(xiàn)某種功能的算法。表達(dá)式有2個(gè)基本屬性:值和類型。如上面的表達(dá)式2+3,它的值為5。根據(jù)操作符的不同,表達(dá)式可以分為多種類型,如:
- 關(guān)系表達(dá)式
- 邏輯表達(dá)式
- 條件表達(dá)式
- 賦值表達(dá)式
- 算術(shù)表達(dá)式
等等。
02. C語言的語句
語句是構(gòu)成程序的基本單元,一般形式如下:表達(dá)式;
**表達(dá)式的后面加一個(gè); 就構(gòu)成了一條基本的語句。**編譯器在編譯程序、解析程序時(shí),不是根據(jù)物理行,而是根據(jù)分號(hào) ; 來判斷一條語句的結(jié)束標(biāo)記的。如 i = 2 + 3; 這條語句,你寫成下面的形式也是可以編譯通過的:
#include <stdio.h>int main(void) {int i = 0;//合法的表達(dá)式i=1+2;return 0; }03. C語言中的代碼塊
不同的語句,使用大括號(hào){}括起來,就構(gòu)成了一個(gè)代碼塊。C 語言允許在代碼塊里定義一個(gè)變量,這個(gè)變量的作用域也僅限于這個(gè)代碼塊內(nèi),因?yàn)榫幾g器就是根據(jù){}來做入棧出棧操作來管理變量的作用域的。如下面的程序:
程序示例
#include <stdio.h>int main(void) {int var = 3;printf("var = %d\n", var);//代碼塊{int var = 88;printf("代碼塊 var = %d\n", var);}printf("var = %d\n", var);return 0; }執(zhí)行結(jié)果
deng@itcast:~/tmp$ gcc 5hello.c deng@itcast:~/tmp$ ./a.out var = 3 代碼塊 var = 88 var = 304. C語言中的語句表達(dá)式
GNU C 對(duì) C 標(biāo)準(zhǔn)作了擴(kuò)展,允許在一個(gè)表達(dá)式里內(nèi)嵌語句,允許在表達(dá)式內(nèi)部使用局部變量、for 循環(huán)和 goto 跳轉(zhuǎn)語句。這樣的表達(dá)式,我們稱之為語句表達(dá)式。語句表達(dá)式的格式如下:
({ 表達(dá)式1; 表達(dá)式2; 表達(dá)式3; ... 表達(dá)式n;})語句表達(dá)式最外面使用小括號(hào)()括起來,里面一對(duì)大括號(hào){}包起來的是代碼塊,代碼塊里允許內(nèi)嵌各種語句。語句的格式可以是 “表達(dá)式;”這種一般格式的語句,也可以是循環(huán)、跳轉(zhuǎn)等語句。
**跟一般表達(dá)式一樣,語句表達(dá)式也有自己的值。語句表達(dá)式的值為內(nèi)嵌語句中最后一個(gè)表達(dá)式的值。**我們舉個(gè)例子,使用語句表達(dá)式求值。
程序示例
#include <stdio.h>int main(void) {int sum = 0;sum = ({int sum = 0;for (int i = 0; i < 100; i++){sum = sum + i;}sum;});printf("sum = %d\n", sum);return 0; }執(zhí)行結(jié)果
deng@itcast:~/tmp$ gcc 6.c deng@itcast:~/tmp$ ./a.out sum = 4950在上面的程序中,通過語句表達(dá)式實(shí)現(xiàn)了從0到99的累加求和,因?yàn)檎Z句表達(dá)式的值等于最后一個(gè)表達(dá)式的值,所以在 for 循環(huán)的后面,我們要添加一個(gè) sum; 語句表示整個(gè)語句表達(dá)式的值。如果不加這一句,你會(huì)發(fā)現(xiàn) sum=0?;蛘吣銓⑦@一行語句改為100; 你會(huì)發(fā)現(xiàn)最后 sum 的值就變成了100,這是因?yàn)檎Z句表達(dá)式的值總等于最后一個(gè)表達(dá)式的值。
goto語句和語句表達(dá)式結(jié)合使用
在語句表達(dá)式內(nèi),我們同樣也可以使用 goto 進(jìn)行跳轉(zhuǎn)。
程序示例
#include <stdio.h>int main(void) {int sum = 0;sum = ({int sum = 0;for (int i = 0; i < 100; i++){sum = sum + i;if (i == 10)goto loop;}sum;});printf("sum = %d\n", sum); loop:printf("goto loop\n");printf("sum = %d\n", sum);return 0; }執(zhí)行結(jié)果
deng@itcast:~/tmp$ gcc 6.c deng@itcast:~/tmp$ ./a.out goto loop sum = 005. 宏中使用語句表達(dá)式
語句表達(dá)式的亮點(diǎn)在于定義復(fù)雜功能的宏。使用語句表達(dá)式來定義宏,不僅可以實(shí)現(xiàn)復(fù)雜的功能,而且還能避免宏定義帶來的歧義和漏洞。下面就以一個(gè)宏定義例子,讓我們來看看語句表達(dá)式在宏定義中的應(yīng)用!
曾經(jīng)面試的過程中,面試官給我出了一道題:
請(qǐng)定義一個(gè)宏,求兩個(gè)數(shù)的最大值。
別看這么簡(jiǎn)單的一個(gè)考題,面試官就能根據(jù)你寫出的宏,來判斷你的 C 語言功底,來決定給不給你 Offer。
初級(jí)程序員
對(duì)于學(xué)過 C 語言的同學(xué),寫出這個(gè)宏基本上不是什么難事,使用條件運(yùn)算符就能完成:
#define MAX(x, y) x > y ? x : y這是最基本的 C 語言語法,如果連這個(gè)也寫不出來,估計(jì)場(chǎng)面會(huì)比較尷尬。面試官為了緩解尷尬,一般會(huì)對(duì)你說:小伙子,你很棒,回去等消息吧,有消息,我們會(huì)通知你!這時(shí)候,你應(yīng)該明白:不用再等了,趕緊把這篇文章看完,接著面下家。這個(gè)宏能寫出來,也不要覺得你很牛 X,因?yàn)檫@只能說明你有了 C 語言的基礎(chǔ),但還有很大的進(jìn)步空間。
其實(shí)上面的寫法在語法上面沒有什么問題,但是在實(shí)際中有bug。
程序示例
#include <stdio.h>#define MAX(x, y) x > y ? x : yint main(void) {printf("Max = %d\n", MAX(1, 2));printf("Max = %d\n", MAX(2, 1));printf("Max = %d\n", MAX(2, 2));printf("Max = %d\n", MAX(1 != 1, 1 != 2));return 0; }執(zhí)行結(jié)果
deng@itcast:~/tmp$ gcc 6.c deng@itcast:~/tmp$ ./a.out Max = 2 Max = 2 Max = 2 Max = 0測(cè)試程序的過程中,我們肯定要把各種可能出現(xiàn)的情況都測(cè)一遍。測(cè)試第10行語句,當(dāng)宏的參數(shù)是一個(gè)表達(dá)式,發(fā)現(xiàn)實(shí)際運(yùn)行結(jié)果為 Max = 0,跟我們預(yù)期結(jié)果 Max = 1 不一樣。這是因?yàn)?#xff0c;宏展開后,就變成了這個(gè)樣子:
printf("Max = %d\n", 1 != 1 > 1 != 2 ? 1 != 1 : 1 != 2);因?yàn)楸容^運(yùn)算符 > 的優(yōu)先級(jí)為6,大于 !=(優(yōu)先級(jí)為7),所以展開的表達(dá)式,運(yùn)算順序發(fā)生了改變,結(jié)果就跟我們的預(yù)期不一樣了。為了避免這種展開錯(cuò)誤,我們可以給宏的參數(shù)加一個(gè)小括號(hào)()來防止展開后,表達(dá)式的運(yùn)算順序發(fā)生變化。這樣的宏才能算一個(gè)合格的宏:
#define MAX(x, y) (x) > (y) ? (x) : (y)程序示例
#include <stdio.h>#define MAX(x, y) (x) > (y) ? (x) : (y)int main(void) {printf("Max = %d\n", MAX(1, 2));printf("Max = %d\n", MAX(2, 1));printf("Max = %d\n", MAX(2, 2));printf("Max = %d\n", MAX(1 != 1, 1 != 2));return 0; }執(zhí)行結(jié)果
deng@itcast:~/tmp$ gcc 6.c deng@itcast:~/tmp$ ./a.out Max = 2 Max = 2 Max = 2 Max = 1上面的宏,只能算合格,但還是存在漏洞。比如,我們使用下面的代碼測(cè)試:
程序示例
#include <stdio.h>#define MAX(x, y) (x) > (y) ? (x) : (y)int main(void) {printf("max = %d\n", 3 + MAX(1, 2));return 0; }測(cè)試結(jié)果
deng@itcast:~/tmp$ gcc 6.c deng@itcast:~/tmp$ ./a.out max = 1在程序中,我們打印表達(dá)式 3 + MAX(1, 2) 的值,預(yù)期結(jié)果應(yīng)該是5,但實(shí)際運(yùn)行結(jié)果卻是1。我們展開后,發(fā)現(xiàn)同樣有問題:
3 + (1) > (2) ? (1) : (2);因?yàn)檫\(yùn)算符 + 的優(yōu)先級(jí)大于比較運(yùn)算符 >,所以這個(gè)表達(dá)式就變?yōu)? > 2 ? 1 : 2,最后結(jié)果為1也就見怪不怪了。此時(shí)我們應(yīng)該繼續(xù)修改這個(gè)宏:
#define MAX(x,y) ((x) > (y) ? (x) : (y))使用小括號(hào)將宏定義包起來,這樣就避免了當(dāng)一個(gè)表達(dá)式同時(shí)含有宏定義和其它高優(yōu)先級(jí)運(yùn)算符時(shí),破壞整個(gè)表達(dá)式的運(yùn)算順序。如果你能寫到這一步,說明你比前面那個(gè)面試的同學(xué)強(qiáng),前面那個(gè)同學(xué)已經(jīng)回去等消息。
中級(jí)程序員
上面的宏,雖然解決了運(yùn)算符優(yōu)先級(jí)帶來的問題,但是仍存在一定的漏洞。比如,我們使用下面的測(cè)試程序來測(cè)試我們定義的宏:
程序示例
#include <stdio.h>#define MAX(x, y) ((x) > (y) ? (x) : (y))int main(void) {int i = 2;int j = 6;printf("max = %d\n", MAX(i++, j++));return 0; }執(zhí)行結(jié)果
deng@itcast:~/tmp$ vim 6.c deng@itcast:~/tmp$ gcc 6.c deng@itcast:~/tmp$ ./a.out max = 7在程序中,我們定義兩個(gè)變量 i 和 j,然后比較兩個(gè)變量的大小,并作自增運(yùn)算。實(shí)際運(yùn)行結(jié)果發(fā)現(xiàn) max = 7,而不是預(yù)期結(jié)果 max = 6。這是因?yàn)樽兞?i 和 j 在宏展開后,做了兩次自增運(yùn)算,導(dǎo)致打印出 i 的值為7。
遇到這種情況,那該怎么辦呢? 這時(shí)候,語句表達(dá)式就該上場(chǎng)了。我們可以使用語句表達(dá)式來定義這個(gè)宏,在語句表達(dá)式中定義兩個(gè)臨時(shí)變量,分別來暫儲(chǔ) i 和 j 的值,然后進(jìn)行比較,這樣就避免了兩次自增、自減問題。
程序示例
#include <stdio.h>#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\n", MAX(i++, j++));return 0; }執(zhí)行結(jié)果
deng@itcast:~/tmp$ gcc 6.c deng@itcast:~/tmp$ ./a.out max = 6在語句表達(dá)式中,我們定義了2個(gè)局部變量 _x、_y 來存儲(chǔ)宏參數(shù) x 和 y 的值,然后使用 _x 和 _y 來比較大小,這樣就避免了 i 和 j 帶來的2次自增運(yùn)算問題。
高級(jí)程序員
在上面這個(gè)宏中,我們定義的兩個(gè)臨時(shí)變量數(shù)據(jù)類型是 int 型,只能比較兩個(gè)整型的數(shù)據(jù)。那對(duì)于其它類型的數(shù)據(jù),就需要重新再定義一個(gè)宏了,這樣太麻煩了!我們可以基于上面的宏繼續(xù)修改,讓它可以支持任意類型的數(shù)據(jù)比較大小:
程序示例
#include <stdio.h>#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\n", MAX(int, i++, j++));printf("max = %lf\n", MAX(float, 3.33, 4.44));return 0; }執(zhí)行結(jié)果
deng@itcast:~/tmp$ ./a.out max = 6 max = 4.440000在這個(gè)宏中,我們添加一個(gè)參數(shù):type,用來指定臨時(shí)變量 _x 和 _y 的類型。這樣,我們?cè)诒容^兩個(gè)數(shù)的大小時(shí),只要將2個(gè)數(shù)據(jù)的類型作為參數(shù)傳給宏,就可以比較任意類型的數(shù)據(jù)了。
上面的宏定義中,我們?cè)黾恿艘粋€(gè)type類型參數(shù),來兼容不同的數(shù)據(jù)類型,其實(shí)我們還有更加牛逼的語法,typeof是GNU C新增的一個(gè)關(guān)鍵字,用來獲取數(shù)據(jù)類型,我們不用傳參進(jìn)去,讓typeof直接獲取!
程序示例
#include <stdio.h>#define MAX(x, y) ({ \typeof(x) _x = x; \typeof(y) _y = y; \(void)(&_x == &_y); \_x > _y ? _x : _y; \})int main(void) {int i = 2;int j = 6;printf("max = %d\n", MAX(i++, j++));printf("max = %lf\n", MAX(3.33, 4.44));return 0; }執(zhí)行結(jié)果
deng@itcast:~/tmp$ gcc 6.c deng@itcast:~/tmp$ ./a.out max = 6 max = 4.440000在這個(gè)宏定義中,使用了 typeof 關(guān)鍵字用來獲取宏的兩個(gè)參數(shù)類型。干貨在**(void) (&x == &y);**這句話,簡(jiǎn)直是天才般的設(shè)計(jì)!一是用來給用戶提示一個(gè)錯(cuò)誤,對(duì)于不同類型的指針比較,編譯器會(huì)給一個(gè)錯(cuò)誤,提示兩種數(shù)據(jù)類型不同;二是,當(dāng)兩個(gè)值比較,比較的結(jié)果沒有用到,有些編譯器可能會(huì)給出一個(gè)warning,加個(gè)(void)后,就可以消除這個(gè)警告!
06. Linux內(nèi)核應(yīng)用示例
Linux內(nèi)核中使用語句表達(dá)式非常多。
#define min(x,y) ({ \typeof(x) _x = (x); \typeof(y) _y = (y); \(void) (&_x == &_y); \_x < _y ? _x : _y; })#define max(x,y) ({ \typeof(x) _x = (x); \typeof(y) _y = (y); \(void) (&_x == &_y); \_x > _y ? _x : _y; })#define min_t(type, a, b) min(((type) a), ((type) b)) #define max_t(type, a, b) max(((type) a), ((type) b))07. 附錄
總結(jié)
以上是生活随笔為你收集整理的【嵌入式】C语言高级编程-语句表达式(03)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【嵌入式】C语言高级编程-数组和结构体初
- 下一篇: 【嵌入式】C语言高级编程-contain