這裡討論的是const在成員函式中的用法
- 1月 09 週三 201310:35
const 在成員函式中的用法
- 1月 09 週三 201310:29
const 在C/C++的用法
const的用法分為以下兩種:
1:在定義變量時使用:
總結:在使用const定義變量時,一定要進行初始化操作,在操作符(*,&)左邊的修飾的是指向的內容,在右邊的是本身。
a: const int a=100; 最簡單的用法,設定說明變數a是一個常數變數。
b: int const b=100; 與a功能相同。
c: const int *a=&b; 指向常數的指針,即指針本身的值是可以改變的,但指向的內容是不能改變的。
d: int const *a=&b; 與c功能相同。
e: int * const a = &b; 常數指針,即指針本身的值是不可改變的,但指向的內容是可改變的。
f: const int * const a = &b; 指向常數的常數指針,即指針本身與指向的內容都是不可改變的。
g: const int &a=100; 常數引用,即不能改變引用的值。
2:在函數用使用:
總結:在函數中使用const,情況與定義變數的情況大致相同。
a: void func(const int a); 做為參數使用,說明函數內是不能修改該參數的。
b: const int func(); 做為返回值使用,說明函數的返回值是不能被修改的。
c: int func() const; 常數函數,說明函數是不能修改該類別中成員的值的,只能用於類別的成員函數中。
(應用原則)
1.
先左由右,看左邊最近的是何種data type,若左邊沒有,則看右邊最近的。
2.
沿著*號劃一條線,
如果const位於*的左側,則const就是用來修飾指標所指向的變數,即指標指向為常量。
如果const位於*的右側,const就是修飾指標本身,即指標本身是常量。
範本
例一: const int * Constant1 --> const int (* Constant1)
例二: int const * Constant2 --> int const (* Constant2)
例三: int * const Constant3 --> (int *) const Constant3 --> const (int *) Constant3
例四: int const * const Constant4 --> const (int *) const Constant4
解答
例一 int值不可被修改
例二 int值不可被修改
例三 指標值不可被修改
例四 int值與指標值皆不可修改
在下面這邊做個總結
記憶體的位置可以變動,但是記憶體的內容不能變動
const Parent* pParent1 = new SubA();
記憶體的位置可以變動,但是記憶體的內容不能變動
Parent const *pParent2 = new SubA();
記憶體的內容可以變動,但是記憶體的位址不能變動
Parent* const pParent3 = new SubA();
記憶體的位址與內容都不能變動
const Parent* const pParent4 = new SubA();
記憶體的位置可以變動,但是記憶體的內容不能變動
const Parent const *pParent5 = new SubA();
- 1月 17 週二 201216:41
stdin、stdout、stderr串流
其實C函式庫中已經宣告好3個FILE *的指標,分別是stdin、stdout、stderr。
stdin standard input 標準輸入串流
stdout standard output 標準輸出串流
stderr standard error 輸出串流且系統會留下紀錄檔
stdin standard input 標準輸入串流
stdout standard output 標準輸出串流
stderr standard error 輸出串流且系統會留下紀錄檔
- 1月 17 週二 201215:37
檔案處理 fopen、fclose
FILE *fopen(const char *filename, const char *mode);
Open file
Opens the file whose name is specified in the parameter filename and associates it with a stream that can be identified in future operations by the FILE object whose pointer is returned. The operations that are allowed on the stream and how these are performed are defined by the mode parameter.
Open file
Opens the file whose name is specified in the parameter filename and associates it with a stream that can be identified in future operations by the FILE object whose pointer is returned. The operations that are allowed on the stream and how these are performed are defined by the mode parameter.
- 1月 17 週二 201215:28
strncmp strcmp
int strcmp (const char *str1, const char *str2);
int strncmp (const char *str1, const char *str2, size_t num);
Compares up to num characters of the C string str1 to those of the C string str2.
- 1月 17 週二 201215:16
變更位置函數 fseek、ftell、rewind
Ref:
http://www.cplusplus.com/reference/
http://jhengjyun.blogspot.com/2009/09/c-fseekftellrewind.html
/*
http://www.cplusplus.com/reference/
http://jhengjyun.blogspot.com/2009/09/c-fseekftellrewind.html
/*
- 1月 17 週二 201215:03
檔案的讀寫 fwrite、fread
//數值不必轉為字元形式,效率較優勢
size_t fwrite(const void *ptr, size_t size, size_t num, FILE *stream);
size_t fread(void *buffer, size_t size, size_t num, FILE *stream);
/*
size_t fwrite(const void *ptr, size_t size, size_t num, FILE *stream);
size_t fread(void *buffer, size_t size, size_t num, FILE *stream);
/*
- 1月 03 週二 201218:30
typedef 知多少
《教學》typedef 知多少?
很多寫 C/C++ 的人都把 typedef 當成#define 來使用。
誠然,像這樣的定義
typedef unsigned short WORD;
就相當於
#define WORD unsigned short
但就本義來說,#define 是字串的取代,例如
#define WORD Hello!
編譯器不會有任何的抗議就通過的,在程式任何使用到 WORD 的地方
編譯器會忠實的用 Hello! 取代 WORD
由於取代的字串可不限於一行且可代入參數,所以#define 可用於 Macro 的定義如
#define max(a,b) (((a) > (b)) ? (a) : (b))
回過頭來說typedef主題,從本義來說typedef是 C/C++ 型態的別名。
所以若你想把
#define WORD Hello!
套在 typedef 的身上:
typedef Hello! WORD;
那麼編譯器會給你一個難堪錯誤訊息!因為 Hello! 不是一個合法的
型態,你不能為它取型態別名。
typedef看來不怎麼有用是吧!?非也,由於所謂 C/C++ 的型態是很廣義的,使用typedef
可以使程式看起來簡潔易懂且不容易出錯。下面是幾個typedef的應用例子:
簡單型態的別名
typedef unsigned char BYTE;//定義無號單字節的型態
typedef unsigned short WORD;//定義無號雙字節的型態
typedef unsigned long DWORD;//定義無號四字節的型態
//嗯!這三行沒什麼大不了,用#define也可以做
結構型態的別名
typedef struct StructTag{
int mA;
int mB;
}STRUCTTAG, *PSTRUCTTAG;
當要建立這個結構的物件時,就可以用別名 STRUCTTAG 和 PSTRUCTTAG,例如
STRUCTTAG StructObj;
PSTRUCTTAG pStructObj;
就相當於
struct StructTag StructObj;
struct StructTag *pStructObj;
函式型態的別名
如果你有一個 library 提供了字串轉整數的函式
int HexToInt(char *str);//十六進位字串轉整數
int DecToInt(char *str);//十進位字串轉整數
當你的程式要使用這些函式時,就必需include這些函式的定義,但用typedef會
更簡潔:
typedef int ToInt(char *str);
//上面這行定義了一個需傳入字串(char *)且返回整數(int)的函式型態別名
//叫 ToInt
ToInt HexToInt,DecToInt;//宣告HexToInt,DecToInt這兩個函式
現在你的程式可以呼叫HexToInt(...),DecToInt(...)了。
在Windows,通常是應用程式呼叫API來工作的,但由於某種需求,Windows API
會反過來呼叫應用程式的函式,這種函式稱之為 callback function。
callback function 對API來說只有該傳入那些參數和返回值型態,沒有函式名稱而用函式
指標來呼叫(事實上Windows也不可能用程式的函式名稱來呼叫)。這時使用函式型態的別名
來宣告函式指標就特別好用了。
舉個例子吧!建立一個新的行程(Thread)的函式:
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc,
LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT
nStackSize = 0, DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );
其中AFX_THREADPROC就是一個函式型態的別名
它的定義是
typedef UINT (AFX_CDECL *AFX_THREADPROC)(LPVOID);
//AFX_THREADPROC 是一個傳入一個指標參數返回UINT型態值的函式
//指標的名稱(AFX_CDEL是標明函式呼叫為 __cdecl(C style calling convention))
用這個宣告,不論API或應用程式本身都很方便,如應用程式可以這樣來
開啟行程:
CWinThread* pMyThread=AfxBeginThread(
(AFX_THREADPROC)MyThreadFunc,............
是不是很簡潔明瞭?嗯... MyThreadFunc 算不算 callback function 有點疑義,但做為 typedef 的使用例子應沒問題的
很多寫 C/C++ 的人都把 typedef 當成#define 來使用。
誠然,像這樣的定義
typedef unsigned short WORD;
就相當於
#define WORD unsigned short
但就本義來說,#define 是字串的取代,例如
#define WORD Hello!
編譯器不會有任何的抗議就通過的,在程式任何使用到 WORD 的地方
編譯器會忠實的用 Hello! 取代 WORD
由於取代的字串可不限於一行且可代入參數,所以#define 可用於 Macro 的定義如
#define max(a,b) (((a) > (b)) ? (a) : (b))
回過頭來說typedef主題,從本義來說typedef是 C/C++ 型態的別名。
所以若你想把
#define WORD Hello!
套在 typedef 的身上:
typedef Hello! WORD;
那麼編譯器會給你一個難堪錯誤訊息!因為 Hello! 不是一個合法的
型態,你不能為它取型態別名。
typedef看來不怎麼有用是吧!?非也,由於所謂 C/C++ 的型態是很廣義的,使用typedef
可以使程式看起來簡潔易懂且不容易出錯。下面是幾個typedef的應用例子:
簡單型態的別名
typedef unsigned char BYTE;//定義無號單字節的型態
typedef unsigned short WORD;//定義無號雙字節的型態
typedef unsigned long DWORD;//定義無號四字節的型態
//嗯!這三行沒什麼大不了,用#define也可以做
結構型態的別名
typedef struct StructTag{
int mA;
int mB;
}STRUCTTAG, *PSTRUCTTAG;
當要建立這個結構的物件時,就可以用別名 STRUCTTAG 和 PSTRUCTTAG,例如
STRUCTTAG StructObj;
PSTRUCTTAG pStructObj;
就相當於
struct StructTag StructObj;
struct StructTag *pStructObj;
函式型態的別名
如果你有一個 library 提供了字串轉整數的函式
int HexToInt(char *str);//十六進位字串轉整數
int DecToInt(char *str);//十進位字串轉整數
當你的程式要使用這些函式時,就必需include這些函式的定義,但用typedef會
更簡潔:
typedef int ToInt(char *str);
//上面這行定義了一個需傳入字串(char *)且返回整數(int)的函式型態別名
//叫 ToInt
ToInt HexToInt,DecToInt;//宣告HexToInt,DecToInt這兩個函式
現在你的程式可以呼叫HexToInt(...),DecToInt(...)了。
在Windows,通常是應用程式呼叫API來工作的,但由於某種需求,Windows API
會反過來呼叫應用程式的函式,這種函式稱之為 callback function。
callback function 對API來說只有該傳入那些參數和返回值型態,沒有函式名稱而用函式
指標來呼叫(事實上Windows也不可能用程式的函式名稱來呼叫)。這時使用函式型態的別名
來宣告函式指標就特別好用了。
舉個例子吧!建立一個新的行程(Thread)的函式:
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc,
LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT
nStackSize = 0, DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );
其中AFX_THREADPROC就是一個函式型態的別名
它的定義是
typedef UINT (AFX_CDECL *AFX_THREADPROC)(LPVOID);
//AFX_THREADPROC 是一個傳入一個指標參數返回UINT型態值的函式
//指標的名稱(AFX_CDEL是標明函式呼叫為 __cdecl(C style calling convention))
用這個宣告,不論API或應用程式本身都很方便,如應用程式可以這樣來
開啟行程:
CWinThread* pMyThread=AfxBeginThread(
(AFX_THREADPROC)MyThreadFunc,............
是不是很簡潔明瞭?嗯... MyThreadFunc 算不算 callback function 有點疑義,但做為 typedef 的使用例子應沒問題的
- 1月 03 週二 201218:22
typedef
關於 typedef 的用法
因為程式裡用了很多,上網查了一下資料,覺得寫的很詳細,就貼上來了
來源一(http://zhidao.baidu.com/question/42813297.html)
Using typedef to Curb Miscreant Code
Typedef 聲明有助於創建平台無關類型,甚至能隱藏複雜和難以理解的語法。
不管怎樣,使用 typedef 能為代碼帶來意想不到的好處,通過本文你可以學習用 typedef 從而使代碼更健壯。
typedef 聲明,簡稱 typedef,為現有類型創建一個新的名字。
比如人們常常使用 typedef 來編寫更美觀和可讀的代碼。
所謂美觀,意指 typedef 能隱藏笨拙的語法構造以及平台相關的數據類型,從而增強可移植性和以及未來的可維護性。
本文下面將竭盡全力來揭示 typedef 強大功能以及如何避免一些常見的陷阱。
如何創建平台無關的數據類型,隱藏笨拙且難以理解的語法? 使用 typedefs 為現有類型創建同義字。
定義易於記憶的類型名
typedef 使用最多的地方是創建易於記憶的類型名,用它來歸檔程序員的意圖。
類型出現在所聲明的變量名字中,位於 ''typedef'' 關鍵字右邊。
例如 : typedef int size;
此聲明定義了一個 int 的同義字,名字為 size。 注意 typedef 並不創建新的類型。
它僅僅為現有類型添加一個同義字。
你可以在任何需要 int 的上下文中使用 size :
void measure(size * psz);
size array[4];
size len = file.getlength();
typedef 還可以掩飾符合類型,如指針和數組。
例如,你不用像下面這樣重複定義有 81 個字符元素的數組 :
char line[81];
char text[81];
定義一個 typedef,每當要用到相同類型和大小的數組時,可以這樣:
typedef char Line[81];
Line text, secondline;
getline(text);
同樣,可以像下面這樣隱藏指針語法:
typedef char * pstr;
int mystrcmp(pstr, pstr);
這裡將帶我們到達第一個 typedef 陷阱。
標準函數 strcmp()有兩個"const char *"類型的參數。
因此,它可能會誤導人們像下面這樣聲明 mystrcmp() :
int mystrcmp(const pstr, const pstr);
這是錯誤的,按照順序,
"const pstr" 被解釋為 "char * const" (一個指向 char 的常量指針),
而不是 "const char *" (指向常量 char 的指針)。
這個問題很容易解決:
typedef const char * cpstr;
int mystrcmp(cpstr, cpstr); // 現在是正確的
記住 :
不管什麼時候,只要為指針聲明 typedef,那麼都要在最終的 typedef 名稱中加一個 const,
以使得該指針本身是常量,而不是對象。
代碼簡化
上面討論的 typedef 行為有點像 #define 巨集,用其實際類型替代同義字。
不同點是 typedef 在編譯時被解釋,因此讓編譯器來應付超越預處理器能力的文本替換。
例如:
typedef int (*PF) (const char *, const char *);
這個聲明引入了 PF 類型作為函數指針的同義字,該函數有兩個 const char * 類型的參數以及一個 int 類型的返回值。
如果要使用下列形式的函數聲明,那麼上述這個 typedef 是不可或缺的:
PF Register(PF pf);
Register() 的參數是一個 PF 類型的回呼(回扣)函數,返回某個函數的地址,其署名與先前註冊的名字相同。
做一次深呼吸。
下面我展示一下如果不用 typedef,我們是如何實現這個聲明的 :
int (*Register (int (*pf)(const char *, const char *)))
(const char *, const char *);
很少有程序員理解它是什麼意思,更不用說這種費解的代碼所帶來的出錯風險了。
顯然,這裡使用 typedef 不是一種特權,而是一種必需。
持懷疑態度的人可能會問 : OK,有人還會寫這樣的代碼嗎?
快速瀏覽一下揭示 signal()函數的頭文件 ,一個有同樣接口的函數。
typedef 和存儲類關鍵字(storage class specifier)
這種說法是不是有點令人驚訝,typedef 就像 auto,extern,mutable,static,和 register 一樣,是一個存儲類關鍵字。
這並是說 typedef 會真正影響對象的存儲特性;
它只是說在語句構成上,typedef 聲明看起來像 static,extern 等類型的變量聲明。
下面將帶到第二個陷阱:
typedef register int FAST_COUNTER; // 錯誤
編譯通不過。
問題出在你不能在聲明中有多個存儲類關鍵字。
因為符號 typedef 已經佔據了存儲類關鍵字的位置,在 typedef 聲明中不能用 register (或任何其它存儲類關鍵字)。
促進跨平台開發
typedef 有另外一個重要的用途,那就是定義機器無關的類型,
例如,你可以定義一個叫 REAL 的浮點類型,在目標機器上它可以i獲得最高的精度:
typedef long double REAL;
在不支持 long double 的機器上,該 typedef 看起來會是下面這樣:
typedef double REAL;
並且,在連 double 都不支持的機器上,該 typedef 看起來會是這樣:、
typedef float REAL;
你不用對源代碼做任何修改,便可以在每一種平台上編譯這個使用 REAL 類型的應用程序。
唯一要改的是 typedef 本身。
在大多數情況下,甚至這個微小的變動完全都可以通過奇妙的條件編譯來自動實現。
不是嗎? 標準庫廣泛地使用 typedef 來創建這樣的平台無關類型:size_t,ptrdiff 和 fpos_t 就是其中的例子。
此外,象 std::string 和 std::ofstream 這樣的 typedef 還隱藏了長長的,難以理解的模板特化語法,例如:basic_string,allocator> 和 basic_ofstream>。
來源二
http://www.ccfans.net/bbs/dispbbs.asp?boardid=30
C語言中typedef用法
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語言編譯器支持這種做法。
3)
struct tagNode
{
char *pItem;
struct tagNode *pNext;
};
typedef struct tagNode *pNode;
3. typedef & #define的問題
有下面兩種定義pStr數據類型的方法,兩者有什麼不同?哪一種更好一點?
typedef char *pStr;
#define pStr char *;
答案與分析 : 通常講,typedef要比#define要好,特別是在有指針的場合。
請看例子:
typedef char *pStr1;
#define pStr2 char *;
pStr1 s1, s2;
pStr2 s3, s4;
在上述的變量定義中,s1、s2、s3都被定義為char *,而s4則定義成了char,不是我們所預期的指針變量,
根本原因就在於#define只是簡單的字符串替換而typedef則是為一個類型起新名字。
#define用法例子:
#define f(x) x*x
main( )
{
int a=6,b=2,c;
c=f(a) / f(b);
printf("%d \\n",c);
}
以下程序的輸出結果是: 36。
因為如此原因,在許多C語言編程規範中提到使用#define定義時,如果定義中包含表達式,必須使用括號,
則上述定義應該如下定義才對:
#define f(x) (x*x)
當然,如果你使用typedef就沒有這樣的問題。
4. typedef & #define的另一例
下面的代碼中編譯器會報一個錯誤,你知道是哪個語句錯了嗎?
typedef char * pStr;
char string[4] = abc;
const char *p1 = string;
const pStr p2 = string;
p1++;
p2++;
答案與分析:
是p2++出錯了。
這個問題再一次提醒我們:typedef和#define不同,它不是簡單的文本替換。
上述代碼中const pStr p2並不等於const char * p2。
const pStr p2和const long x本質上沒有區別,都是對變量進行只讀限制,
只不過此處變量p2的數據類型是我們自己定義的而不是系統固有類型而已。
因此,const pStr p2的含義是:限定數據類型為char *的變量p2為只讀,因此p2++錯誤。
#define與typedef引申談
1) #define 巨集定義有一個特別的長處:可以使用 #ifdef ,#ifndef等來進行邏輯判斷,還可以使用#undef來取消定義。
2) typedef也有一個特別的長處:它符合範圍規則,使用typedef定義的變量類型其作用範圍限制在所定義的函數或者文件內(取決於此變量定義的位置),而宏定義則沒有這種特性。
5. typedef & 複雜的變量聲明
在編程實踐中,尤其是看別人代碼的時候,常常會遇到比較複雜的變量聲明,使用typedef作簡化自有其價值,比如:
下面是三個變量的聲明,我想使用typdef分別給它們定義一個別名,請問該如何做?
1:int *(*a[5])(int, char*);
2:void (*b[10]) (void (*)());
3. doube(*)() (*pa)[9];
答案與分析 :
對複雜變量建立一個類型別名的方法很簡單,你只要在傳統的變量聲明表達式裡用類型名替代變量名,
然後把關鍵字typedef加在該語句的開頭就行了。
int *(*a[5])(int, char*);
//pFun是我們建的一個類型別名
typedef int *(*pFun)(int, char*);
//使用定義的新類型來聲明對象,等價於int* (*a[5])(int, char*);
pFun a[5];
void (*b[10]) (void (*)());
//首先為上面表達式藍色部分聲明一個新類型
typedef void (*pFunParam)();
//整體聲明一個新類型
typedef void (*pFun)(pFunParam);
//使用定義的新類型來聲明對象,等價於void (*b[10]) (void (*)());
pFun b[10];
doube(*)() (*pa)[9];
//首先為上面表達式藍色部分聲明一個新類型
typedef double(*pFun)();
//整體聲明一個新類型
typedef pFun (*pFunParam)[9];
//使用定義的新類型來聲明對象,等價於doube(*)() (*pa)[9];
pFunParam pa;
因為程式裡用了很多,上網查了一下資料,覺得寫的很詳細,就貼上來了
來源一(http://zhidao.baidu.com/question/42813297.html)
Using typedef to Curb Miscreant Code
Typedef 聲明有助於創建平台無關類型,甚至能隱藏複雜和難以理解的語法。
不管怎樣,使用 typedef 能為代碼帶來意想不到的好處,通過本文你可以學習用 typedef 從而使代碼更健壯。
typedef 聲明,簡稱 typedef,為現有類型創建一個新的名字。
比如人們常常使用 typedef 來編寫更美觀和可讀的代碼。
所謂美觀,意指 typedef 能隱藏笨拙的語法構造以及平台相關的數據類型,從而增強可移植性和以及未來的可維護性。
本文下面將竭盡全力來揭示 typedef 強大功能以及如何避免一些常見的陷阱。
如何創建平台無關的數據類型,隱藏笨拙且難以理解的語法? 使用 typedefs 為現有類型創建同義字。
定義易於記憶的類型名
typedef 使用最多的地方是創建易於記憶的類型名,用它來歸檔程序員的意圖。
類型出現在所聲明的變量名字中,位於 ''typedef'' 關鍵字右邊。
例如 : typedef int size;
此聲明定義了一個 int 的同義字,名字為 size。 注意 typedef 並不創建新的類型。
它僅僅為現有類型添加一個同義字。
你可以在任何需要 int 的上下文中使用 size :
void measure(size * psz);
size array[4];
size len = file.getlength();
typedef 還可以掩飾符合類型,如指針和數組。
例如,你不用像下面這樣重複定義有 81 個字符元素的數組 :
char line[81];
char text[81];
定義一個 typedef,每當要用到相同類型和大小的數組時,可以這樣:
typedef char Line[81];
Line text, secondline;
getline(text);
同樣,可以像下面這樣隱藏指針語法:
typedef char * pstr;
int mystrcmp(pstr, pstr);
這裡將帶我們到達第一個 typedef 陷阱。
標準函數 strcmp()有兩個"const char *"類型的參數。
因此,它可能會誤導人們像下面這樣聲明 mystrcmp() :
int mystrcmp(const pstr, const pstr);
這是錯誤的,按照順序,
"const pstr" 被解釋為 "char * const" (一個指向 char 的常量指針),
而不是 "const char *" (指向常量 char 的指針)。
這個問題很容易解決:
typedef const char * cpstr;
int mystrcmp(cpstr, cpstr); // 現在是正確的
記住 :
不管什麼時候,只要為指針聲明 typedef,那麼都要在最終的 typedef 名稱中加一個 const,
以使得該指針本身是常量,而不是對象。
代碼簡化
上面討論的 typedef 行為有點像 #define 巨集,用其實際類型替代同義字。
不同點是 typedef 在編譯時被解釋,因此讓編譯器來應付超越預處理器能力的文本替換。
例如:
typedef int (*PF) (const char *, const char *);
這個聲明引入了 PF 類型作為函數指針的同義字,該函數有兩個 const char * 類型的參數以及一個 int 類型的返回值。
如果要使用下列形式的函數聲明,那麼上述這個 typedef 是不可或缺的:
PF Register(PF pf);
Register() 的參數是一個 PF 類型的回呼(回扣)函數,返回某個函數的地址,其署名與先前註冊的名字相同。
做一次深呼吸。
下面我展示一下如果不用 typedef,我們是如何實現這個聲明的 :
int (*Register (int (*pf)(const char *, const char *)))
(const char *, const char *);
很少有程序員理解它是什麼意思,更不用說這種費解的代碼所帶來的出錯風險了。
顯然,這裡使用 typedef 不是一種特權,而是一種必需。
持懷疑態度的人可能會問 : OK,有人還會寫這樣的代碼嗎?
快速瀏覽一下揭示 signal()函數的頭文件 ,一個有同樣接口的函數。
typedef 和存儲類關鍵字(storage class specifier)
這種說法是不是有點令人驚訝,typedef 就像 auto,extern,mutable,static,和 register 一樣,是一個存儲類關鍵字。
這並是說 typedef 會真正影響對象的存儲特性;
它只是說在語句構成上,typedef 聲明看起來像 static,extern 等類型的變量聲明。
下面將帶到第二個陷阱:
typedef register int FAST_COUNTER; // 錯誤
編譯通不過。
問題出在你不能在聲明中有多個存儲類關鍵字。
因為符號 typedef 已經佔據了存儲類關鍵字的位置,在 typedef 聲明中不能用 register (或任何其它存儲類關鍵字)。
促進跨平台開發
typedef 有另外一個重要的用途,那就是定義機器無關的類型,
例如,你可以定義一個叫 REAL 的浮點類型,在目標機器上它可以i獲得最高的精度:
typedef long double REAL;
在不支持 long double 的機器上,該 typedef 看起來會是下面這樣:
typedef double REAL;
並且,在連 double 都不支持的機器上,該 typedef 看起來會是這樣:、
typedef float REAL;
你不用對源代碼做任何修改,便可以在每一種平台上編譯這個使用 REAL 類型的應用程序。
唯一要改的是 typedef 本身。
在大多數情況下,甚至這個微小的變動完全都可以通過奇妙的條件編譯來自動實現。
不是嗎? 標準庫廣泛地使用 typedef 來創建這樣的平台無關類型:size_t,ptrdiff 和 fpos_t 就是其中的例子。
此外,象 std::string 和 std::ofstream 這樣的 typedef 還隱藏了長長的,難以理解的模板特化語法,例如:basic_string,allocator> 和 basic_ofstream>。
來源二
http://www.ccfans.net/bbs/dispbbs.asp?boardid=30
C語言中typedef用法
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語言編譯器支持這種做法。
3)
struct tagNode
{
char *pItem;
struct tagNode *pNext;
};
typedef struct tagNode *pNode;
3. typedef & #define的問題
有下面兩種定義pStr數據類型的方法,兩者有什麼不同?哪一種更好一點?
typedef char *pStr;
#define pStr char *;
答案與分析 : 通常講,typedef要比#define要好,特別是在有指針的場合。
請看例子:
typedef char *pStr1;
#define pStr2 char *;
pStr1 s1, s2;
pStr2 s3, s4;
在上述的變量定義中,s1、s2、s3都被定義為char *,而s4則定義成了char,不是我們所預期的指針變量,
根本原因就在於#define只是簡單的字符串替換而typedef則是為一個類型起新名字。
#define用法例子:
#define f(x) x*x
main( )
{
int a=6,b=2,c;
c=f(a) / f(b);
printf("%d \\n",c);
}
以下程序的輸出結果是: 36。
因為如此原因,在許多C語言編程規範中提到使用#define定義時,如果定義中包含表達式,必須使用括號,
則上述定義應該如下定義才對:
#define f(x) (x*x)
當然,如果你使用typedef就沒有這樣的問題。
4. typedef & #define的另一例
下面的代碼中編譯器會報一個錯誤,你知道是哪個語句錯了嗎?
typedef char * pStr;
char string[4] = abc;
const char *p1 = string;
const pStr p2 = string;
p1++;
p2++;
答案與分析:
是p2++出錯了。
這個問題再一次提醒我們:typedef和#define不同,它不是簡單的文本替換。
上述代碼中const pStr p2並不等於const char * p2。
const pStr p2和const long x本質上沒有區別,都是對變量進行只讀限制,
只不過此處變量p2的數據類型是我們自己定義的而不是系統固有類型而已。
因此,const pStr p2的含義是:限定數據類型為char *的變量p2為只讀,因此p2++錯誤。
#define與typedef引申談
1) #define 巨集定義有一個特別的長處:可以使用 #ifdef ,#ifndef等來進行邏輯判斷,還可以使用#undef來取消定義。
2) typedef也有一個特別的長處:它符合範圍規則,使用typedef定義的變量類型其作用範圍限制在所定義的函數或者文件內(取決於此變量定義的位置),而宏定義則沒有這種特性。
5. typedef & 複雜的變量聲明
在編程實踐中,尤其是看別人代碼的時候,常常會遇到比較複雜的變量聲明,使用typedef作簡化自有其價值,比如:
下面是三個變量的聲明,我想使用typdef分別給它們定義一個別名,請問該如何做?
1:int *(*a[5])(int, char*);
2:void (*b[10]) (void (*)());
3. doube(*)() (*pa)[9];
答案與分析 :
對複雜變量建立一個類型別名的方法很簡單,你只要在傳統的變量聲明表達式裡用類型名替代變量名,
然後把關鍵字typedef加在該語句的開頭就行了。
int *(*a[5])(int, char*);
//pFun是我們建的一個類型別名
typedef int *(*pFun)(int, char*);
//使用定義的新類型來聲明對象,等價於int* (*a[5])(int, char*);
pFun a[5];
void (*b[10]) (void (*)());
//首先為上面表達式藍色部分聲明一個新類型
typedef void (*pFunParam)();
//整體聲明一個新類型
typedef void (*pFun)(pFunParam);
//使用定義的新類型來聲明對象,等價於void (*b[10]) (void (*)());
pFun b[10];
doube(*)() (*pa)[9];
//首先為上面表達式藍色部分聲明一個新類型
typedef double(*pFun)();
//整體聲明一個新類型
typedef pFun (*pFunParam)[9];
//使用定義的新類型來聲明對象,等價於doube(*)() (*pa)[9];
pFunParam pa;
