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++)
用法詳解:
- #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
- #define的變體,即#ifdef,可以實現加入自己需要的模塊(源文件)
[例子] 在源文件中加入
#ifdef MYSELF_H
#include "myself.c"
#endif
可以實現在源文件中加入myself.c的代碼,將其實現的功能加進來, 即加入了myself模塊。
- #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;
答案與分析:
-
typedef的最簡單使用
typedef long byte_4; //給已知數據類型long起個新名字,叫byte_4。
-
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 )