typedef和#define的用法、區別以及陷阱!

2021-02-19 嵌入式資訊精選

在C語言編程中,typedef和#define是最常用語句,可能很多工作過兩三年的工程師都沒有去深究過它們的一些用法和區別。

一、typedef的用法

在C/C++語言中,typedef常用來定義一個標識符及關鍵字的別名,它是語言編譯過程的一部分,但它並不實際分配內存空間,比如:

typedef  int    INT;typedef  (int*)   pINT;typedef unsigned int  uint32_t

typedef可以增強程序的可讀性,以及標識符的靈活性,但它也有「非直觀性」等缺點。

二、#define的用法

#define為一宏定義語句,通常用它來定義常量(包括無參量與帶參量),以及用來實現那些「表面似和善、背後一長串」的宏,它本身並不在編譯過程中進行,而是在這之前(預處理過程)就已經完成了,但也因此難以發現潛在的錯誤及其它代碼維護問題,比如:

#define   INT        int#define   TRUE       1#define   Add(a,b)   ((a)+(b));#define   Loop_10    for (int i=0; i<10; i++)

在Scott Meyer的Effective C++一書的條款1中有關於#define語句弊端的分析,以及好的替代方法,大家可參看。

三、typedef與#define的區別

從以上的概念便也能基本清楚,typedef只是為了增加可讀性而為標識符另起的新名稱(僅僅只是個別名),而#define原本在C中是為了定義常量。到了C++,const、enum、inline的出現使它也漸漸成為了起別名的工具。

有時很容易搞不清楚 #define 與 typedef 兩者到底該用哪個好,如#define INT int這樣的語句,用typedef一樣可以完成,用哪個好呢?

我主張用typedef,因為在早期的許多C編譯器中這條語句是非法的,只是現今的編譯器又做了擴充。為了儘可能地兼容,一般都遵循#define定義「可讀」的常量以及一些宏語句的任務,而typedef則常用來定義關鍵字、冗長的類型的別名。

宏定義只是簡單的字符串代換(原地擴展),而typedef則不是原地擴展,它的新名字具有一定的封裝性,以致於新命名的標識符具有更易定義變量的功能。

比如代碼:

以及:

效果相同?實則不同!實踐中見差別:pINT a,b;的效果同int *a; int *b;表示定義了兩個整型指針變量。而pINT2 a,b;的效果同int *a, b;

表示定義了一個整型指針變量a和整型變量b。

用途一:

定義一種類型的別名,而不只是簡單的宏替換。

可以用作同時聲明指針型的多個對象。比如:

char*   pa,   pb; //這多數不符合我們的意圖,它只聲明了一個指向字符變量的指針,和一個字符變量;

以下則可行:

typedef  char*  PCHAR;PCHAR  pa,  pb;        //同時聲明了兩個指向字符變量的指針

雖然下面(代碼)方式也可行,但相對來說沒有用typedef的形式直觀,尤其在需要大量指針的地方,typedef的方式更省事。

用途二:

用在舊的C代碼中(具體多舊沒有查),幫助struct。

以前的代碼中,聲明struct新對象時,必須要帶上struct,即形式為:

struct   結構名   對象名,如:

struct   tagPOINT1{int   x;int   y;};
struct tagPOINT1 p1;

而在C++中,則可以直接寫:結構名   對象名,即:

估計某人覺得經常多寫一個struct太麻煩了,於是就發明了:

typedef   struct   tagPOINT{int   x;int   y;}POINT;
POINT p1; // 這樣就比原來的方式少寫了一個struct,比較省事,尤其在大量使用的時候

或許,在C++中,typedef的這種用途二不是很大,但是理解了它,對掌握以前的舊代碼還是有幫助的,畢竟我們在項目中有可能會遇到較早些年代遺留下來的代碼。

用途三:

用typedef來定義與平臺無關的類型。

比如定義一個叫 REAL 的浮點類型,在目標平臺一上,讓它表示最高精度的類型為:

typedef   long   double   REAL;

在不支持 long   double 的平臺二上,改為:

在連 double 都不支持的平臺三上,改為:

也就是說,當跨平臺時,只要改下 typedef 本身就行,不用對其他源碼做任何修改。標準庫就廣泛使用了這個技巧,比如size_t。

另外,因為typedef是定義了一種類型的新別名,不是簡單的字符串替換,所以它比宏來得穩健(雖然用宏有時也可以完成以上的用途)。

用途四:

為複雜的聲明定義一個新的簡單的別名。

方法是:在原來的聲明裡逐步用別名替換一部分複雜聲明,如此循環,把帶變量名的部分留到最後替換,得到的就是原聲明的最簡化版。舉例如下一些情況。

1.原聲明:

int   *(*a[5])(int,   char*);

變量名為a,直接用一個新別名pFun替換a就可以了:

typedef   int   *(*pFun)(int,   char*);

原聲明的最簡化版:

2.原聲明:

void   (*b[10])   (void   (*)());

變量名為b,先替換右邊部分括號裡的,pFunParam為別名一:

typedef   void   (*pFunParam)();

再替換左邊的變量b,pFunx為別名二:

typedef   void   (*pFunx)(pFunParam);

原聲明的最簡化版:

3.原聲明:

變量名為e,先替換左邊部分,pFuny為別名一:

typedef   double(*pFuny)();

再替換右邊的變量e,pFunParamy為別名二

typedef   pFuny   (*pFunParamy)[9];

原聲明的最簡化版:

理解複雜聲明可用的「右左法則」:從變量名看起,先往右,再往左,碰到一個圓括號就調轉閱讀的方向;括號內分析完就跳出括號,還是按先右後左的順序,如此循環,直到整個聲明分析完。舉例:

首先找到變量名func,外面有一對圓括號,而且左邊是一個*號,這說明func是一個指針;然後跳出這個圓括號,先看右邊,又遇到圓括號,這說明(*func)是一個函數,所以func是一個指向這類函數的指針,即函數指針,這類函數具有int*類型的形參,返回值類型是int。

func右邊是一個[]運算符,說明func是具有5個元素的數組;func的左邊有一個*,說明func的元素是指針(注意這裡的*不是修飾func,而是修飾func[5]的,原因是[]運算符優先級比*高,func先跟[]結合)。跳出這個括號,看右邊,又遇到圓括號,說明func數組的元素是函數類型的指針,它指向的函數具有int*類型的形參,返回值類型為int。

也可以記住2個模式:

type   (*)(....)函數指針  

type   (*)[]數組指針

陷阱一:

記住,typedef是定義了一種類型的新別名,不同於宏,它不是簡單的字符串替換。比如先定義:

然後:

int   mystrcmp(const   PSTR,   const   PSTR);

const   PSTR實際上相當於const   char*嗎?

不是的,它實際上相當於char*   const。

原因在於const給予了整個指針本身以常量性,也就是形成了常量指針char*   const。

簡單來說,記住當const和typedef一起出現時,typedef不會是簡單的字符串替換就行。

陷阱二:

typedef在語法上是一個存儲類的關鍵字(如auto、extern、static、register等一樣),雖然它並不真正影響對象的存儲特性,如:

typedef   static   int   INT2;   //不可行

編譯將失敗,會提示「指定了一個以上的存儲類」。

1.用於MCU,基於FreeRTOS的micro(輕量級)ROS!

2.RT-Thread團隊回應社區關切:鴻蒙OS帶來的影響-合作遠遠大於競爭

3.從嵌入式編程中感悟「棧」為何方神聖?

4.bug粉碎機之volatile的那些坑

5.118億電晶體!蘋果A14首秀,這顆全球首發的5nm晶片有多香?

6.嵌入式C語言原始碼優化方案(非編譯器優化)

免責聲明:本文系網絡轉載,版權歸原作者所有。如涉及作品版權問題,請與我們聯繫,我們將根據您提供的版權證明材料確認版權並支付稿酬或者刪除內容。

相關焦點

  • typedef和#define的用法、區別,以及陷阱
    素材來源:網絡編輯整理:strongerHuang在C語言編程中,typedef 和 #define是最常用語句,可能很多工作過兩三年的工程師都沒有去深究過它們的一些用法和區別
  • C語言#define和typedef的用法區別,以及陷阱
    和 #define是最常用語句,可能很多工作過幾年的工程師都沒有去深究過它們的一些用法和區別。#define為一宏定義語句,通常用它來定義常量(包括無參量與帶參量),以及用來實現那些「表面似和善、背後一長串」的宏,它本身並不在編譯過程中進行,而是在這之前(預處理過程)就已經完成了,但也因此難以發現潛在的錯誤及其它代碼維護問題,比如:#define INT int#define TRUE 1#define Add
  • typedef用法 與#define
    本文引用地址:http://www.eepw.com.cn/article/201612/324896.htm一、typedef的用法人說typedef的使用可以編寫更加美觀和可讀的代碼,原因是typedef可以隱藏笨拙的語法結構以及平臺相關的數據類型,從而增加可移植性及未來的可維護性。
  • C語言中typedef與define的這些區別值得關注
    typedef用來定義自己習慣使用的數據類型名稱,可以替代自己所熟悉的基本類型、數組類型、指針類型以及自己定義的結構體類型、共用體類型、枚舉類型等。一旦我們在程序中使用typedef定義了自己的數據類型名稱,我們就可以像使用int、float和double等基本數據類型一樣來使用它。
  • C語言typedef VS define,孰優孰劣?
    ,但您也可以使用小寫字母,如下:typedef unsigned char byte;您也可以使用 typedef 來為用戶自定義的數據類型取一個新的名字。,它會產生下列結果:書標題 : C 教程書作者 : Runoob書類目 : 程式語言書 ID : 12345define一般用法:
  • 關於typedef的用法總結
    與 #define的區別:  案例一:  通常講,typedef要比#define要好,特別是在有指針的場合。這個問題再一次提醒我們:typedef和#define不同,它不是簡單的  文本替換。上述代碼中const pStr p2並不等於const char *p2。const pStr p2和  const long x本質上沒有區別,都是對變量進行只讀限制,只不過此處變量p2的數據類  型是我們自己定義的而不是系統固有類型而已。
  • struct和typedef struct
    分三塊來講述:  1 首先://注意在C和C++裡不同    在C中定義一個結構體類型要用typedef:    typedef struct Student    {    int a;    }Stu;    於是在聲明變量的時候就可:Stu stu1;(如果沒有typedef就必須用
  • C語言之類型定義(typedef)
    從51過來的讀者可能會說,幹嘛要用 typedef,使用 #define (關於#define可以查看#define小節)也能有相同的效果。確實是,但是 #define 嚴格來說它只是用來替換的,而typedef 是專業的。所以使用 #define 可能一不小心就會給你挖坑了。
  • 單片機C語言的一些誤用和知識
    C語言雖然很強大但是他也有不少陷阱,所以我發這篇帖子有兩個目的:一是把C語言一些誤用易錯的地方總結一下,二是把C語言一些基本語法總結一下。2.1.typedef  C語言除了直接使用標準的類型名(如 int char float double)和自己聲明的結構體、共用體、指針、枚舉類型外,還可以用typedef聲明新的類型名來代替現有的類型名。
  • C語言中typedef用法總結,看完就能像編程老手一樣熟練運用
    請看下文C語言中typedef關鍵字應用比較常見,許多C語言初學者對它的用法不甚了解。事實上,我們可以用typedef來定義自己習慣使用的數據類型名稱,可以替代自己所熟悉的基本類型、數組類型、指針類型以及自己定義的結構體類型、共用體類型、枚舉類型等。一旦我們在程序中定義了自己的數據類型名稱,我們就可以像使用int、float和double等基本數據類型一樣來使用它。實際使用中,typedef有以下幾種主要形式。
  • 為什麼很多人編程喜歡用typedef?如何避免濫用?
    在一些網絡協議、網卡驅動等對字節寬度、大小端比較關注的地方,也會經常看到typedef使用得很頻繁。2.3 比宏定義更好用C語言的預處理指令#define用來定義一個宏,而typedef則用來聲明一種類型的別名。typedef跟宏相比,不僅僅是簡單的字符串替換,可以使用該類型同時定義多個同類型對象。
  • 【編程經驗】C語言中的宏陷阱 #define SQU(x) x*x
    求兩個或幾個數的乘積: #define SQU(x) x*x我們正常使用沒有問題:但如果這樣寫呢?哎呀,竟然不是100了,難道SQU(10)和SQU(5+5)不等價嗎?5+5不是10嗎?的確,這裡SQU(10)和SQU(5+5)的確不等價!原因在於,宏定義的本質是文本替換!
  • 細說And與Or的用法,以及它們的區別在哪裡,如何使用?
    學習英語的時候,我們除了需要認識單詞之外,我們還一定要留意單詞的用法。對於我們來說,我們需要熟悉應用的英語單詞有動詞和一些連詞,像形容詞、副詞之類的一般沒有什麼特殊的用法,我們只需記住它的中文意思,以及詞性即可。連詞and和or ,我們都很熟悉吧?
  • C語言宏定義的特殊用法以及避坑指南
    總結一下C語言中宏的一些特殊用法和幾個容易踩的坑。由於本文主要參考GCC文檔,某些細節(如宏參數中的空格是否處理之類)在別的編譯器可能有細微差別,請參考相應文檔。宏基礎宏僅僅是在C預處理階段的一種文本替換工具,編譯完之後對二進位代碼不可見。
  • –ed形容詞和–ing形容詞的用法區別
    frightened look 害怕的表情(指帶有這種「表情」的人感到害怕) a frightening look 嚇人的表情(指這種「表情」令人害怕) an excited talk 心情激動的談話(指「談話」的人心情激動) an exciting talk 令人激動的談話(指聽「談話」的人心情激動) 由於傳統的觀點說得太絕對,有時做起題來還可能會因此掉入命題人的陷阱
  • 「C語言指針」起別名關鍵字typedef和結構體類型的恩怨情仇
    在上一篇文章中,我們簡要了解了鍊表的前導知識——結構體和它的眾多小細節。其中提到了起別名關鍵字typedef,在那裡我們只是簡單介紹過:在實際工作中,程式設計師們普遍使用它,但是並未展開去講細節。所以本文決定沿襲上一篇文章的思路,結合一些程序實例,細緻地展開討論typedef和結構體相碰產生的奇妙反應。
  • 快速上手系列-C語言之typedef
    那具體是什麼類型的數據,它們可能長這個樣子:typedef int INT32;typedef short INT16;既然typedef的作用是給已有的類型重新定義一個新的名字,那麼如何使用呢?先說說typedef定義類型的方法:1、用typedef聲明新的類型名代替已有類型名2、typedef語句的的一般形式:typedef 原數據類型 新的類型名如在32位平臺我們給int重新取名INT32,形如:typedef int INT32;嗯,大概就是這個樣子的。