你想知道的do{...}while(0)的作用,都在這裡了

来源:https://www.cnblogs.com/Sharemaker/archive/2023/02/21/17142670.html
-Advertisement-
Play Games

0、引言 我們在嵌入式開發的過程中,經常可以碰到在一些巨集定義或者是代碼段中使用了do {...} while(0)的語句,從語義上理解,do {...} while(0)內的邏輯就只執行一次,並沒有迴圈執行,粗略看來,似乎畫蛇添足了,那麼為什麼還需要在只執行一次的邏輯外面加上一層do {...} w ...


0、引言        

        我們在嵌入式開發的過程中,經常可以碰到在一些巨集定義或者是代碼段中使用了do {...} while(0)的語句,從語義上理解,do {...} while(0)內的邏輯就只執行一次,並沒有迴圈執行,粗略看來,似乎畫蛇添足了,那麼為什麼還需要在只執行一次的邏輯外面加上一層do {...} while(0)語句呢?實際上,在這些邏輯中使用do {...} while(0)的作用遠大於美化你的代碼,下麵就來看看實際的使用場景。


1、用於定義一個作用域,避免替換的時候出錯

        我們都知道,在程式中如果一些常量參數或者代碼語句反覆出現,就可以使用巨集定義來替代。預處理階段,對程式中所有出現的“巨集名”,預處理器都會用巨集定義中的字元串替代,這稱為“巨集替換”或“巨集展開”。

        這樣做可提高程式的通用性和易讀性,減少不一致性,一個較好的巨集名可以更好的讓讀者理解常量參數的含義;同時程式易於修改,我們僅需要改變一個巨集定義,就可以改變整個程式中出現的所有該常量或者語句。

        但是有時可能程式代碼段中,出現多條語句重覆連續的使用,這樣我們就可以嘗試使用一個複雜的巨集來替換。你有可能會這樣定義:

1 #define REPLACE_FUN() funA(); funB()

   本意是在程式中當出現funA()和funB()多條語句連續使用時,使用REPLACE_FUN()來替換。

1 if(判斷條件)
2     REPLACE_FUN();

        但是實際上在預處理的時候,巨集展開替換後變成了:

1 if(判斷條件)
2    funA();
3 funB();   //此處funB()一定會執行,造成邏輯錯誤

        可以看出,funB()不會按照判斷條件才去執行。而是變成了一條獨立的語句,而如果在巨集中使用括弧:

1 #define REPLACE_FUN() {funA(); funB();}

        我們一般的代碼習慣都會在語句的末尾加上分號,因此也會出錯:

1 if(判斷條件)
2     REPLACE_FUN();
3 //巨集展開後為:
4 if(判斷條件)
5 {
6     funA();
7     funB();
8 };    //此處替換後多一個分號;導致編譯報錯

        因此,針對這種多條重覆語句的連續使用,如果想用巨集替換實現這個作用域的功能,就可以考慮使用do {...} while(0)語句:

 1 define REPLACE_FUN() \
 2         do{ \
 3             funA();\
 4             funB();\
 5           }while(0)\
 6 //巨集展開前為:         
 7 if(判斷條件)
 8     REPLACE_FUN();
 9 //巨集展開後為:
10 if(判斷條件)
11      do{
12           funA();
13           funB();
14      }while(0);    //根據判斷條件,正確執行了一次邏輯


2、避免goto語句的使用

        goto語句也稱為無條件轉移語句,使用後可以從多重迴圈或者多個判斷中直接跳出。對於如下例子:

 1 void fun(int a)
 2 {
 3    if(1 == a)
 4    {
 5        ...//todo
 6        goto exit;
 7    }  
 8    if(2 == a)
 9    {
10      ...//todo
11      goto exit;
12    }
13 exit:
14    ...//todo
15    printf("a is error"\n);
16 }

        但是為了程式結構的清晰,還是要儘量限制goto語句的使用,我們可以使用do {...} while(0)結構配合break跳出單層的迴圈的方法來替代這種goto的用法。

 1 int fun(int a)
 2 {
 3    do{
 4        if(1 == a)
 5        {
 6          ...//todo
 7          break;
 8        }  
 9        if(2 == a)
10        {
11          ...//todo
12          break;
13        }
14    }while(0);
15    ...//todo
16    printf("a is error"\n);
17 }

3、定義一個單獨的函數塊來實現複雜的操作

        當某個函數程式功能較為複雜,在該函數的代碼段中如果不再單獨定義一個函數實現部分邏輯,可以使用do {...} while(0)作為一個代碼塊,將想要實現的邏輯放在do {...} while(0)中,同時在該在do {...} while(0)代碼塊中定義的變數,可以不用考慮和函數之前或者之後的變數名重覆衝突的問題。但是為了代碼的易讀性,還是儘量聲明不同的變數名。

 1 int a;
 2 char b;
 3 int func()
 4 {
 5     int a = 3;
 6     char b = 5;
 7     do{
 8         int a;
 9         char b;
10         ......//todo
11     }while(0);    
12 }

4、避免空巨集的警告

        有的時候,程式為了不同的平臺移植或者不同架構的限制,很多時候會先定義空巨集,後續再根據實際的需要看是否定義具體內容。但是在編譯的時候,這些空巨集可能會給出warning,為了避免這樣的warning,我們可以使用do{...}while(0)來定義空巨集,這種情況不太常見,因為有很多編譯器已經支持空巨集。

1 //空巨集
2 #define EMPTY_FUN
3 //增加do{...}while(0)來定義空巨集
4 #define EMPTY_FUN do{}while(0) //避免了可能的編譯warning

更多技術內容和書籍資料獲取,入群技術交流敬請關註“明解嵌入式”

本文來自博客園,作者:Sharemaker,轉載請註明原文鏈接:https://www.cnblogs.com/Sharemaker/p/17142670.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 代碼組織 代碼按一下順序組織: @import 變數聲明 樣式聲明 1 @import "mixins/size.less"; 2 @default-text-color: #333; 3 .page { 4 width: 960px; 5 margin: 0 auto; 6 } @import 語 ...
  • vue2 使用 cesium 篇 今天好好寫一篇哈,之前寫的半死不活的。首先說明:這篇博文是我邊做邊寫的,小白也是,實現效果會同時發佈截圖,如果沒有實現也會說明,僅僅作為技術積累,選擇性分享,不做教學哈。不好別噴。 安裝 cesium 這個就很簡單,只需要一句簡簡單單的命令就可以實現在 vue 項目 ...
  • 本文是系列第二篇。系列文章: 現代圖片性能優化及體驗優化指南 - 圖片類型及 Picture 標簽的使用 圖片資源,在我們的業務中可謂是占據了非常大頭的一環,尤其是其對帶寬的消耗是十分巨大的。 對圖片的性能優化及體驗優化在今天就顯得尤為重要。本文,就將從各個方面闡述,在各種新特性滿頭飛的今天,我們可 ...
  • 一、背景 遠程服務將電腦程式的工作範圍從單機擴展到網路,從本地延伸至遠程,是構建分散式系統的首要基礎。遠程服務調用(Remote Procedure Call,RPC)在電腦科學中已經存在了超過四十年時間。但很多人無法明確區分RPC與Rest。本文就講一講RPC和Rest的本質區別。 二、分析 ...
  • 談到java中的併發,我們就避不開線程之間的同步和協作問題,談到線程同步和協作我們就不能不談談jdk中提供的AbstractQueuedSynchronizer(翻譯過來就是抽象的隊列同步器)機制; (一)、AQS中的state和Node含義: AQS中提供了一個int volatile state ...
  • RabbitMQ 配置環境 安裝 erlang環境以及RabbitMQ RabbitMQ埠號: 5672 去官網下載 https://www.rabbitmq.com 然後重啟RabbitMQ服務 RabbitMQ安裝教程 開放埠15672 這裡,通過http://IP地址:15672 進行We ...
  • 在如 rails 這樣的開源庫中,我們常常見到這樣的一類寫法: class_eval <<-RUBY, lxx_file, lxx_line + 1 def xxx # do something here end RUBY 令人困惑不已。 不過這裡的知識點非常簡單,只要掌握了 heredoc 與 e ...
  • 前言 文章主要說明在FA中的中文函數的代碼實現,不僅要知道用法,更要知其實現的原理。前面的用法為FA中的用法,僅支持在FA中使用,源碼可以在其它app中使用。 非原創,代碼收集整理於網路。 進入子頁面 進入子頁面("頁面名稱") lua源碼: function 進入子頁面(name,param) i ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...