C語言#define和typedef的用法區別,以及陷阱

2021-03-01 嵌入式專欄

關注+星標公眾,不錯過精彩內容

編排 | strongerHuang

微信公眾號 | 嵌入式專欄

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

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

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

#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原本在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。

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

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

以下則可行:

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;

或許,在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等一樣),雖然它並不真正影響對象的存儲特性,如:

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

免責聲明:本文素材來源網絡,版權歸原作者所有。如涉及作品版權問題,請與我聯繫刪除。-- END --

後臺回復『嵌入式C語言』閱讀更多相關文章。

歡迎關注我的公眾號回復「加群」按規則加入技術交流群,回復「1024」查看更多內容。

點擊「閱讀原文」查看更多分享,歡迎點分享、收藏、點讚、在看。 

相關焦點

  • typedef和#define的用法、區別,以及陷阱
    素材來源:網絡編輯整理:strongerHuang在C語言編程中,typedef 和 #define是最常用語句,可能很多工作過兩三年的工程師都沒有去深究過它們的一些用法和區別
  • typedef和#define的用法、區別以及陷阱!
    在C語言編程中,typedef和#define是最常用語句,可能很多工作過兩三年的工程師都沒有去深究過它們的一些用法和區別。
  • C語言中typedef與define的這些區別值得關注
    typedef用來定義自己習慣使用的數據類型名稱,可以替代自己所熟悉的基本類型、數組類型、指針類型以及自己定義的結構體類型、共用體類型、枚舉類型等。一旦我們在程序中使用typedef定義了自己的數據類型名稱,我們就可以像使用int、float和double等基本數據類型一樣來使用它。
  • typedef用法 與#define
    本文引用地址:http://www.eepw.com.cn/article/201612/324896.htm一、typedef的用法人說typedef的使用可以編寫更加美觀和可讀的代碼,原因是typedef可以隱藏笨拙的語法結構以及平臺相關的數據類型,從而增加可移植性及未來的可維護性。
  • C語言typedef VS define,孰優孰劣?
    typedefC 語言提供了 typedef關鍵字,您可以使用它來為類型取一個新的名字。,它會產生下列結果:書標題 : C 教程書作者 : Runoob書類目 : 程式語言書 ID : 12345define一般用法:
  • 關於typedef的用法總結
    與 #define的區別:  案例一:  通常講,typedef要比#define要好,特別是在有指針的場合。這個問題再一次提醒我們:typedef和#define不同,它不是簡單的  文本替換。上述代碼中const pStr p2並不等於const char *p2。const pStr p2和  const long x本質上沒有區別,都是對變量進行只讀限制,只不過此處變量p2的數據類  型是我們自己定義的而不是系統固有類型而已。
  • C語言之類型定義(typedef)
    我們知道C語言定義數據類型的時候只定義了它們之間的關係,但卻沒有具體定義它們的大小。比如 short 的長度只規定了不大於 int,long的長度不小於 int,int是多大也沒確定,所以你會看到51單片機的int大小為兩個字節,而在stm32中的長度為 4 字節。
  • struct和typedef struct
    typedef struct和struct的區別:    typedef struct tagMyStruct    {     int iNum基本解釋typedef為C語言的關鍵字,作用是為一種數據類型定義一個新名字。這裡的數據類型包括內部數據類型(int,char等)和自定義的數據類型(struct等)。在編程中使用typedef目的一般有兩個,一個是給變量一個易記且意義明確的新名字,另一個是簡化一些比較複雜的類型聲明。至於typedef有什麼微妙之處,請你接著看下面對幾個問題的具體闡述。
  • 單片機C語言的一些誤用和知識
    我絕得語言之所以能稱之為語言,它肯定是一種工具一種相互交流相互通信相互傳達之間的意圖的工具,作為語言那肯定得有自己的語法,要想相互交流肯定得先學好它的語法吧(比如像表達式,函數,循環,指針)我稱之為C語言的語法。C語言雖然很強大但是他也有不少陷阱,所以我發這篇帖子有兩個目的:一是把C語言一些誤用易錯的地方總結一下,二是把C語言一些基本語法總結一下。
  • 如何寫出高效優美的單片機C語言代碼?
    下面發一些我在網上看到的技巧和自己的一些經驗來和大家分享;1、如果可以的話少用庫函數,便於不同的mcu和編譯器間的移植2、選擇合適的算法和數據結構應該熟悉算法語言,知道各種算法的優缺點,具體資料請參見相應的參考資料,有很多計算機書籍上都有介紹。
  • C語言中typedef用法總結,看完就能像編程老手一樣熟練運用
    請看下文C語言中typedef關鍵字應用比較常見,許多C語言初學者對它的用法不甚了解。事實上,我們可以用typedef來定義自己習慣使用的數據類型名稱,可以替代自己所熟悉的基本類型、數組類型、指針類型以及自己定義的結構體類型、共用體類型、枚舉類型等。一旦我們在程序中定義了自己的數據類型名稱,我們就可以像使用int、float和double等基本數據類型一樣來使用它。實際使用中,typedef有以下幾種主要形式。
  • c語言之共用體union、枚舉、大小端模式
    上一個專題我們詳細的分享了c語言裡面的結構體用法,讀者在看這些用法的時候,可以一邊看一邊試驗,掌握了這些基本用法就完全夠用了,當然在以後的工作中
  • C語言之精華總結
    關於虛擬地址(線性地址)和真實地址的區別以及實現,可以參考《Linux原始碼情景分析》的第二章存儲管理,在網際網路上關於這個主題的文章汗牛充棟,你也可以google一下。 但請注意,在C++中指向對象成員的指針(pointer to member data or member function)的大小不一定是4個字節。
  • 總結嵌入式開發中的C語言知識點
    )數據存儲(auto, static, extern,const, register,volatile,restricted)結構(struct, enum, union,typedef)位操作和邏輯運算(<<, >>, &, |, ~,^, &&)預處理(#define, #include, #
  • 為什麼很多人編程喜歡用typedef?如何避免濫用?
    C語言中已經存在的一個類型起一個新名字。2.2 增加代碼的可移植性C語言的int類型,我們知道,在不同的編譯器和平臺下,所分配的存儲字長不一樣:可能是2個字節,可能是4個字節,也有可能是8個字節。如果我們在代碼中想定義一個固定長度的數據類型,此時使用int,在不同的平臺環境下運行可能會出現問題。為了應付各種不同「脾氣」的編譯器,最好的辦法就是使用自定義數據類型,而不是使用C語言的內置類型。
  • C語言陷阱與技巧第18節,函數式宏定義的「缺陷」,沒有參數類型檢查...
    在之前的文章裡,我們曾討論C語言程序開發中 define 宏定義的「陷阱」之一就是可能會產生多次「副作用」,這也是C語言中函數式宏定義與真正函數的主要區別之一。顯然,define 宏定義的這種「陷阱」會導致程序存在隱患,而且這種隱患造成的危害不亞於「野指針」。
  • C語言編程核心要點
    類型C是強類型語言,有short、long、int、char、float、double等build-in數據類型,類型是貫穿c語言整個課程的核心概念。struct、union、enum屬於c的構造類型,用於自定義類型,擴充類型系統。變量變量用來保存數據,數據是操作的對象,變量的變字意味著它可以在運行時被修改。
  • 《邊學邊用攻破C語言》第17集 宏定義define的用法
    《邊學邊用攻破C語言》是專門為單片機初學者準備的C語言基礎視頻教學課程,是科技老頑童
  • c語言重點知識點複習資料(5)
    2)strlen 和 sizeof的區別也是考試的重點; 3)define f(x)(x*x) 和 define f(x) x*x 之間的差別。一定要好好的注意這寫容易錯的地方,替換的時候有括號和沒有括號是很大的區別。
  • C語言基礎知識學習經典入門
    >11 C語言函數指針... 612 C語言typedef關鍵字... 613 C語言面向對象 – 思維方式... 714 C語言引入自定義頭文件... 915 C語言字符串操作 - sprintf 1016 C語言文件操作 – fprintf、 fread. 1017 C語言實現猜數字小遊戲... 11