1. 程式人生 > >【轉】 do {...} while (0) 在巨集定義中的作用

【轉】 do {...} while (0) 在巨集定義中的作用

如果你是一名C程式設計師,你肯定很熟悉巨集,它們非常強大,如果正確使用可以讓你的工作事半功倍。然而,如果你在定義巨集時很隨意沒有認真檢查,那麼它們可能使你發狂,浪費N多時間。在很多的C程式中,你可能會看到許多看起來不是那麼直接的較特殊的巨集定義。下面就是一個例子:

1

2

#define __set_task_state(tsk, state_value)      \

    do { (tsk)->state = (state_value); } while (0)

Linux核心和其它一些著名的C庫中有許多使用do{...}while(0)的巨集定義。這種巨集的用途是什麼?有什麼好處?

Google的Robert Love(先前從事Linux核心開發)給我們解答如下:

do{...}while(0)在C中是唯一的構造程式,讓你定義的巨集總是以相同的方式工作,這樣不管怎麼使用巨集(尤其在沒有用大括號包圍呼叫巨集的語句),巨集後面的分號也是相同的效果。

這句話聽起來可能有些拗口,其實用一句話概括就是:使用do{...}while(0)構造後的巨集定義不會受到大括號、分號等的影響,總是會按你期望的方式呼叫執行。

例如:

1

#define foo(x) bar(x); baz(x)

然後你可能這樣呼叫:

1

foo(wolf);

這將被巨集擴充套件為:

1

bar(wolf); baz(wolf);

這的確是我們期望的正確輸出。下面看看如果我們這樣呼叫:

1

2

if (!feral)

    foo(wolf);

那麼擴充套件後可能就不是你所期望的結果。上面語句將擴充套件為:

1

2

3

if (!feral)

    bar(wolf);

baz(wolf);

顯而易見,這是錯誤的,也是大家經常易犯的錯誤之一。

幾乎在所有的情況下,期望寫多語句巨集來達到正確的結果是不可能的。你不能讓巨集像函式一樣行為——在沒有do/while(0)的情況下。

如果我們使用do{...}while(0)來重新定義巨集,即:

1

#define foo(x) do { bar(x); baz(x); } while (0)

現在,該語句功能上等價於前者,do能確保大括號裡的邏輯能被執行,而while(0)能確保該邏輯只被執行一次,即與沒有迴圈時一樣。

對於上面的if語句,將會被擴充套件為:

1

2

if (!feral)

    do { bar(wolf); baz(wolf); } while (0);

從語義上講,它與下面的語句是等價的:

1

2

3

4

if (!feral) {

    bar(wolf);

    baz(wolf);

}

這裡你可能感到迷惑不解了,為什麼不用大括號直接把巨集包圍起來呢?為什麼非得使用do/while(0)邏輯呢?

例如,我們用大括號來定義巨集如下:

1

#define foo(x)  { bar(x); baz(x); }

這對於上面舉的if語句的確能被正確擴充套件,但是如果我們有下面的語句呼叫呢:

1

2

3

4

if (!feral)

    foo(wolf);

else

    bin(wolf);

巨集擴充套件後將變成:

1

2

3

4

5

6

if (!feral) {

    bar(wolf);

    baz(wolf);

};

else

    bin(wolf);

大家可以看出,這就有語法錯誤了。

總結:Linux和其它程式碼庫裡的巨集都用do/while(0)來包圍執行邏輯,因為它能確保巨集的行為總是相同的,而不管在呼叫程式碼中使用了多少分號和大括號。

相關推薦

do {...} while (0) 在巨集定義作用

如果你是一名C程式設計師,你肯定很熟悉巨集,它們非常強大,如果正確使用可以讓你的工作事半功倍。然而,如果你在定義巨集時很隨意沒有認真檢查,那麼它們可能使你發狂,浪費N多時間。在很多的C程式中,你可能會看到許多看起來不是那麼直接的較特殊的巨集定義。下面就是一個例子:

Android 4.0 Launcher2源碼分析——啟動過程分析

handler flag 這一 第一次啟動 asynctask pla size ontouch wait Android的應用程序的入口定義在AndroidManifest.xml文件中可以找出:[html] <manifest xmlns:android="htt

CentOS 7.0 安裝Redis 3.2.1詳細過程和使用常見問題

nec count ges des useful 內存 warning before outside http://www.linuxidc.com/Linux/2016-09/135071.htm 環境:CentOS 7.0 Redis 3.2.1 Redis的安裝與啟動

博客園自定義樣式修改標簽頁的icon圖標

dbo 代碼 分享 新的 ner href get create 樣式 有沒有發現大多數的博客園標簽右上角都是一個小礦工,千篇一律沒有什麽特色,想不想設置一個像我一樣的個性化icon圖標呢? 按照以下四步你也可以實現自定義標簽圖標。 From To 第一步:挑挑揀揀 選

函式的宣告和定義

一、函式的宣告 1.在C語言中,函式的定義順序是有講究的:預設情況下,只有後面定義的函式才可以呼叫前面定義過的函式 1 int sum(int a, int b) { 2 return a + b; 3 } 4 5 int main() 6 { 7 int c = sum(1, 4

C# 在webapi項目配置Swagger

rect static void target 來源 public 圖片 網址 class 以前在做WebAPI調用測試時,一直在使用Fiddler測試工具了,而且這個用起來比較繁瑣,需要各種配置,並且不直觀,還有一點是還得弄明白URL地址和要傳遞的參數,然後才能

JS.JS事件處理函數return的作用

檢測 也會 post 繼續 .cn add AR 當前 進行 1、js事件處理函數中return的作用 - AnswerCard - 博客園.html(https://www.cnblogs.com/answercard/p/5255230.html) 2、網頁內容保存:

Golang學習之同一個package函式互相呼叫的問題

問題程式碼如下 a.go: package main func main(){ Test() } b.go: package main import "fmt" func Test(){ fmt.Println("Hello World!") } 這

do...while(0)在巨集定義的巧妙用法

大家都知道,do…while(condition)可以表示迴圈,但你有沒有遇到在一些巨集定義中可以不用迴圈的地方,也用到了 do…while.比如:   #define DELETE_POINTER(p) \ do \ { \ if

巨集定義#和##的使用

https://www.cnblogs.com/zhongzhe/p/3892682.html   #的功能是將其後面的巨集引數進行字串化操作(Stringfication),簡單說就是在對它所引用的巨集變數通過替換後在其左右各加上一個雙引號 ##被稱為連線符(concatenator),用來

巨集定義的特殊引數(#、##、...和__VA_ARGS__)

轉自:http://blog.csdn.net/cqupt_chen/article/details/8055215 最近在android的某個程式碼的標頭檔案中發現很多__VA_ARGS__,google一下.還是比較有用.附帶其它巨集定義引數,一起記錄之. 1.  

nginx自定義500,502,504錯誤頁面無法跳

header cli targe head href 錯誤 error tom htm 1、自定一個頁面,這個頁面是一個鏈接地址可以直接訪問的。 以下是nginx的配置: location / { proxy_pass http://tomcat_

修真院“善良”系列之十八WEB程序員從零開始到就業的全資料V1.0——只看這一篇就夠了!

absolute feed 自己 session rem 好的 ans 一個 css樣式 這是兩年以來,修真院收集整理的學習資料順序。以CSS15個任務,JS15個任務為基礎,分別依據要完成任務的不同的技能點,我們整理出來了這麽一篇在學習的時候需要看到的資料。這是Versi

Linux下的兩個經典宏定義

unsigned 結果 type gen pause 5-0 成員變量 32位系統 取出 轉自:http://www.linuxidc.com/Linux/2015-07/120014.htm 本文首先介紹Linux下的經典宏定義,感受極客的智慧,然後根據該經典定義為下篇

ArcGIS Server10.1之服務新特性(WMTS1.0.0

class href 知識 技術分享 restful cgi art 存在 alt http://blog.csdn.net/esrichinacd/article/details/7825587 ArcGIS Server10.1正式支持OGC的WMTS1.0.0版

Spring MVC系列(五)之自定義數據綁定---HandlerMethodArgumentResolver

開閉 src pat 獲取參數 mvc .net 定義 開閉原則 淺析 介紹 前面幾節我們介紹了Spring MVC的幾種常見的數據綁定的方法,可以靈活地獲取用戶請求中的參數,例如@PathVariable,@ModelAttribute,@RequestPar

性能測試報告模板 V1.0

行程 3.1 進程 壓力測試 分析 每一個 觀測 試用 資源 1. 測試項目概述與測試目的 1.1 項目概述   本部分主要是針對即將進行壓力測試的對象(接口、模塊、進程或系統)進行概要的說明,讓人明白該測試對象的主要功能與作用及相關背景。 1.2 測試目標   簡要列出進

編寫高質量代碼改善C#程序的157個建議——建議7: 將0值作為枚舉的默認值

enum 整型 沒有 spa day rda alt 編寫 它的 建議7: 將0值作為枚舉的默認值 允許使用的枚舉類型有byte、sbyte、short、ushort、int、uint、long和ulong。應該始終將0值作為枚舉類型的默認值。不過,這樣做不是因為允許使

用宏定義代替printf函數

tar 版本 data eas article target else define ref 問題提出 有時候我們想用宏定義來決定是編譯debug版本的代碼還是release的代碼,dubug版本的代碼會通過printf打印調試信息,release版本的代碼則不會。我們總不

焦慮、渴望掙紮的我們,如何定義成功和失敗?

cse 處理 很好 以及 國家 媒體 你我 slim 今天  我們總在一個人失敗的時候低估他,而在成功的時候又過於追捧,我們不吝對所謂的成功者送上無數鮮花和掌聲,而對所謂的失敗者則冷眼相待,嗤之以鼻。   英國作家阿蘭·德波頓(Alain de Botton),帶領我們檢視