Typedef Struct 用法詳解


Typedef Struct 用法詳解

一、typedef的用法

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

實例像

typedef int INT;
typedef int ARRAY[10];
typedef (int*) pINT;

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++) 
用法詳解:
  1. #define 的變體,即 #ifndef,可以防止頭頭文件的重復引用。

#ifdef和 #define組合,一般用於頭文件中,用以實現防止多個文件對此同一個頭文件的重復引用.實際使用中,即使你的頭文件暫時沒有被多個文件所引用,為了增加程序可讀性,移植性,健壯性等,還是最好都加上。其用法一般為:

#ifndef <標識>
#define <標識>
………   // include or define sth.
#endif

<標識>在理論上來說可以是自由命名的,但每個頭文件的這個“標識”都應該是唯一的 to void the definition duplication。but normallz, 標識的命名規則一般是頭文件名全大寫,前后加下划線,並把文件名中的“.”也變成下划線,如:stdio.h對應的就是:

#ifndef _STDIO_H_
#define _STDIO_H_
………   // include or define sth.
#endif
  1. #define的變體,即#ifdef,可以實現加入自己需要的模塊(源文件)

[例子] 在源文件中加入

#ifdef MYSELF_H
#include "myself.c"
#endif

可以實現在源文件中加入myself.c的代碼,將其實現的功能加進來, 即加入了myself模塊。

  1. #define可以進行宏定義常量

可以對一些常見的變量,字符串等,進行宏定義,系統在編譯期間,就會自動替換 如果不進行宏定義,一般如果此類變量,字符串等,需要修改,就需要對源文件中它們出現的地方一一修改,效率比較低,而此種宏定義后,只需要修改一次,實現批量修改,效率較高.而且有些數字或字符很麻煩,每次都要輸入,就顯得很繁瑣,而且容易出錯,而采取此宏定義,就很方便和易於維護.

[例子]

#define PI 3.1415926

[注意事項]
(1) 宏定義中的變量,約定俗成用大寫,以此與小寫的普通變量區分開來.當然如果你故意小寫,也是合法的.不過如果你想讓你寫的程序具有高可讀性,那最好遵守此約定.
(2) #define的行尾,沒有分號”;”,有些人不注意,會畫蛇添足地加上.有些公司招聘時候的筆試,也會考察這個細節.
(3) 如果后面的宏定義中的變量和前面的有內在聯系,那么后面的宏定義變量最好用前面的表示

[例子]

#define PI 3.1415926
#define RADIUS 5

而在表達該圓的面積的時候,就可以用下面的表示了:

#define AREA ((PI)*( RADIUS)*( RADIUS))
//此處加括號是為了避免后面提到的一種邊界效應

[缺點]

宏定義有一些缺點:

(1) 無法對宏定義中的變量進行類型檢查 此缺點,是相對於const變量來說的

[define與const的區別的簡單總結]
define定義的變量,是Compile-Time時期的變量,系統在編譯時候,就將其全部替換,而不會對其變量進行類型等屬性檢查,相對不是很安全,可能存在潛在的問題,而沒有發現. 正因為其僅僅是編譯時期替換,所以其定義的變量,是不會在運行時候分配內存的,不占用內存空間. const定義的變量,是 Run-Time時期的變量,如果類型不匹配,系統在運行時候,就會發現並提示或報錯,對應的,const變量在運行時期,也是一種變量,系統會為其分配內存.

(2) 邊界效應

A. 未加括號帶來的邊界效應 由於宏定義的時候,其各個分量未加括號,而在使用宏定義的時候,傳遞的參數是變量的表達式,然后經過系統展開后,由於優先級的原因,導致其結果不是你所希望的.

[例子]

#define MUL(A,B) A*B

而在使用的時候,這樣的調用:

int a=1,b=2,c=3,d=0;
d=MUL(a+b,c)

經過編譯時候展開,就變成了

d=a+b*c

而不是我們所希望的

d=(a+b)*c

[解決辦法]

其解決辦法也很簡單,就是給每個分量,都加上括號,就可以避免此類問題 即,在宏定義的時候,如此定義:

#define MUL(A,B) ((A)*(B))

B. 在define數據類型的時候, 未加括號帶來的問題 在用define進行新的數據類型定義的時候,由於未加括號,會出現你所未預料到的結果. 此點其實就是上面說的邊界效應,之所以將此點單獨說一下,是由於此點不是普通計算結果的問題,而是數據類型的問題,問題相對更嚴重. 也是筆者在看《想成為嵌入式程序員應知道的0x10個基本問題》的時候,看到其作者提到的這個問題,此處就用其例子解釋如下:

[例子]

#define dPS struct s *   //注意末尾無分號

當使用的時候,遇到:

dPS p1,p2;

的時候,經過編譯時候替換擴展,就變成了

struct s* p1,p2;

而p2就不是我們所希望的s的指針類型了,而是一個普通的s數據結構類型的了.產生了邊界效應.

[解決辦法]

對應的解決辦法也很簡單,就是,遇到此類新數據類型定義的時候,還是用typedef 將上述宏定義改為:

typedef struct s * tPS; // 注意末尾有分號

而后的使用:

tPS p1,p2;

就正常了.

C. 特殊情況時候,加了括號也無法避免錯誤 在宏定義中出現++或—之類的操作符的時候,即使加括號,也無法避免其中的問題

[例子]

#define MIN(A,B) ((A)<(B)?(A):(B))

如果進行如此調用

int a=1,b=3,min=0;
min=MIN(a++,b);

經過編譯替換展開后,就成了

max=((a++)< (b)?(a++):(b))

計算出來的結果,就是

min=3

而不是我們所要的

min=1

此類問題無法避免,除非程序員在調用宏的時候,自己多加注意,盡量避免此類調用. about how to use the Macro of the mostly used function, like min() and max() ,please refer this: eliminate the side effect of Micro in C 【后記20100813】 如果想要min的宏定義,避免傳入a++,b++之類所導致的副作用,那么可以參考最新的Linux內核中的定義,如下:

#define min(x, y) ({     
typeof(x) _x = (x);    
typeof(y) _y = (y);    
(void) (&_x == &_y);   
_x < _y ? _x : _y; })

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

三、typedef與#define的區別

從以上的概念便也能基本清楚,typedef只是為了增加可讀性而為標識符另起的新名稱(僅僅只是個別名),而#define原本在C中是為了定義常量 ,到了C++,const、enum、inline的出現使它也漸漸成為了起別名的工具。有時很容易搞不清楚與typedef兩者到底該用哪個好,如#define INT int這樣的語句,用typedef一樣可以完成,用哪個好呢?我主張用typedef 因為在早期的許多C編譯器中這條語句是非法的,只是現今的編譯器又做了擴充。為了盡可能地兼容,一般都遵循#define定義“可讀”的常量以及一些 宏語句 的任務,而typedef則常用來定義關鍵字、冗長的類型的別名 。 宏定義只是簡單的字符串代換(原地擴展),而typedef則不是原地擴展,它的新名字具有一定的封裝性,以致於新命名的標識符具有更易定義變量的功能。請看上面第一大點代碼的第三行:

typedef (int*) pINT; 

以及下面這行:

#define pINT2 int* 

效果相同?實則不同!實踐中見差別:

pINT a,b;的效果同int *a; int *b;表示定義了兩個整型指針變量。

pINT2 a,b;的效果同int *a, b; 表示定義了一個整型指針變量a和整型變量b。

注意:兩者還有一個行尾;號的區別哦!

四、typedef struct與struct的區別

1.基本解釋

typedef為C語言的關鍵字,作用是為一種數據類型定義一個新名字。這里的數據類型包括內部數據類型(int,char等)和自定義的數據類型(struct等)。

在編程中使用typedef目的一般有兩個,一個是給變量一個易記且意義明確的新名字,另一個是簡化一些比較復雜的類型聲明。

至於typedef有什么微妙之處,請你接着看下面對幾個問題的具體闡述。

2.typedef & 結構的問題

當用下面的代碼定義一個結構時,編譯器報了一個錯誤,為什么呢?莫非C語言不允許在結構中包含指向它自己的指針嗎?請你先猜想一下,然后看下文說明:

typedef struct tagNode
{
 char *pItem;
 pNode pNext;
} *pNode;

答案與分析:

  1. typedef的最簡單使用

    typedef long byte_4;
    //給已知數據類型long起個新名字,叫byte_4。
    
  2. typedef與結構結合使用

    typedef struct tagMyStruct
    { 
     int iNum;
     long lLength;
    } MyStruct;
    

    這語句實際上完成兩個操作:
    1) 定義一個新的結構類型

    struct tagMyStruct
    { 
     int iNum; 
     long lLength; 
    };
    

    分析:tagMyStruct稱為“tag”,即“標簽”,實際上是一個臨時名字,struct 關鍵字和tagMyStruct一起,構成了這個結構類型,不論是否有typedef,這個結構都存在。

    我們可以用struct tagMyStruct varName來定義變量,但要注意,使用tagMyStruct varName來定義變量是不對的,因為struct 和tagMyStruct合在一起才能表示一個結構類型。

    2) typedef為這個新的結構起了一個名字,叫MyStruct。

    typedef struct tagMyStruct MyStruct;
    

    因此,MyStruct實際上相當於struct tagMyStruct,我們可以使用MyStruct varName來定義變量。

    C語言當然允許在結構中包含指向它自己的指針,我們可以在建立鏈表等數據結構的實現上看到無數這樣的例子,上述代碼的根本問題在於typedef的應用。

    根據我們上面的闡述可以知道:新結構建立的過程中遇到了pNext域的聲明,類型是pNode,要知道pNode表示的是類型的新名字,那么在類型本身還沒有建立完成的時候,這個類型的新名字也還不存在,也就是說這個時候編譯器根本不認識pNode。

    解決這個問題的方法有多種:

    1)、

    typedef struct tagNode 
    {
     char *pItem;
      struct tagNode *pNext;
    } *pNode;
    

    2)、

    typedef struct tagNode *pNode;
    struct tagNode 
    {
     char *pItem;
     pNode pNext;
    };
    

    注意:在這個例子中,你用typedef給一個還未完全聲明的類型起新名字。C語言編譯器支持這種做法。

五、在C和C++中struct和typedef struct的區別

在C和C++有三種定義結構的方法。
typedef struct {

int data;

int text;

} S1;

//這種方法可以在c或者c++中定義一個S1結構

struct S2 {

int data;

int text;

};

// 這種定義方式只能在C++中使用,而如果用在C中,那么編譯器會報錯

struct {

int data;

int text;

} S3;

//這種方法並沒有定義一個結構,而是定義了一個s3的結構變量,編譯器會為s3內存。

void main()

{

S1 mine1;// OK ,S1 是一個類型

S2 mine2;// OK,S2 是一個類型

S3 mine3;// OK,S3 不是一個類型

S1.data = 5;// ERROR S1 是一個類型

S2.data = 5;// ERROR S2 是一個類型

S3.data = 5;// OK S3是一個變量

}

另外,對與在結構中定義結構本身的變量也有幾種寫法

struct S6 {

S6* ptr;

};

// 這種寫法只能在C++中使用

typedef struct {

S7* ptr;

} S7;

// 這是一種在C和C++中都是錯誤的定義

如果在C中,我們可以使用這樣一個“曲線救國的方法“

typedef struct tagS8{

tagS8 * ptr;

} S8;

六、struct和typedef struct

分三塊來講述:
1 首先: 在C中定義一個結構體類型要用typedef:

typedef struct Student
{
    int a;
}Stu;

於是在聲明變量的時候就可:

Stu stu1;

如果沒有typedef就必須用:

struct Student stu1;

來聲明

這里的Stu實際上就是struct Student的別名。

另外這里也可以不寫Student(於是也不能struct Student stu1;了)

typedef struct
{
    int a;
}Stu;

但在c++里很簡單,直接

struct Student
{
    int a;
};

於是就定義了結構體類型Student,聲明變量時直接Student stu2;

===============================================================

2其次:在c++中如果用typedef的話,又會造成區別:

struct Student 
{ 
int a; 
}stu1;//stu1是一個變量 
typedef struct Student2 
{ 
int a; 
}stu2;//stu2是一個結構體類型 

使用時可以直接訪問stu1.a 但是stu2則必須先 stu2 s2; 然后 s2.a=10;

===========================================

七、C/C++中typedef struct和struct的用法

struct _x1 { ...}x1; 

typedef struct _x2{ ...} x2; 

有什么不同?

其實, 前者是定義了類x1和x1的對象實例x1, 后者是定義了類x2和x2的類別名x2 ,

所以它們在使用過程中是有取別的.請看實例1.

[知識點]

結構也是一種數據類型, 可以使用結構變量, 因此, 象其它 類型的變量一樣, 在使用結構變量時要先對其定義。

定義結構變量的一般格式為:

struct 結構名 

{ 

類型 變量名; 

類型 變量名; 

... 

} 結構變量;  //結構體的變量

結構名是結構的標識符,不是變量名。

另一種常用格式為:

typedef struct 結構名 

{    

類型 變量名; 

類型 變量名; 

... 

} 結構別名;  //結構別名:只是結構的標示符——結構體類型

另外注意: 在C中,struct不能包含函數。在C++中,對struct進行了擴展,可以包含函數。

======================================================================

實例1: struct.cpp

#include <iostream> 

using namespace std; 

typedef struct _point{ 

int x; 

int y; 

}point; //定義類,給類一個別名 

struct _hello{ 

int x,y; 

} hello; //同時定義類和對象 

int main() 

{ 

point pt1; 

pt1.x = 2; 

pt1.y = 5; 

cout<< "ptpt1.x=" << pt1.x << "pt.y=" <<pt1.y <<endl; 

//hello pt2; 

//pt2.x = 8; 

//pt2.y =10; 

//cout<<"pt2pt2.x="<< pt2.x <<"pt2.y="<<pt2.y <<endl; 

//上面的hello pt2;這一行編譯將不能通過. 為什么? 

//因為hello是被定義了的對象實例了. 

//正確做法如下: 用hello.x和hello.y 

hello.x = 8; 

hello.y = 10; 

cout<< "hellohello.x=" << hello.x << "hello.y=" <<hello.y <<endl; 

return 0; 

}

版權聲明:本文為博主原創文章,未經博主允許不得轉載。By PengCoX ( Pengc825@foxmail.com )


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM