1. 程式人生 > >struct結構體的初始化及typedef的理解總結

struct結構體的初始化及typedef的理解總結

struct結構體是C語言中非常重要的複合型別,初始化的方法很多,下面對這些方法進行總結,便於以後查閱。

一、gcc擴充套件方式(不知道該如何命名)

  1. #include <stdio.h>
  2. struct mych {  
  3.     int k;  
  4. };  
  5. struct myst {  
  6.     int         i;    
  7.     int         j;    
  8.     struct mych ch;   
  9. };  
  10. int main(void)   
  11. {  
  12.     struct myst st = {   
  13.         .i = 1,  
  14.         .j = 2,  
  15.         .ch = {   
  16.             .k = 3   
  17.         }     
  18.     };    
  19.     printf("st.i = %d, st.j = %d, st.ch.k = %d\n", st.i, st.j, st.ch.k);  
  20.     return 0;  
  21. }  

這種方式是在成員前面加上“."來初始化,這種方式比較直觀,但是屬於gcc擴充套件,可移植性較差。但是如果是完全面向Linux的開發,這種方式還是比較好的。這是在linux核心中發現的,具體可以參考Linux核心中/net/ipv4目錄中的arp.c檔案

二、使用{}進行初始化

  1. struct myst st = { 1 ,2, {4}};  
這種方式輸入C標準的用法,可移植性好,但是結構體型別複雜時,可讀性很差

三、使用庫函式

 常用的函式有兩個:memset和bzero。用法如下:

  1. memset(&st, 0, sizeof (st));  
  2. bzero(&set, sizeof (st));  

memset和bzero的主要區別是:引數個數不同;memset 需要三個引數,其中第二個引數是&st指向的記憶體中要初始化的值,而bzero使用0來初始化

typedef總結及用法:

不管實在C還是C++程式碼中,typedef這個詞都不少見,當然出現頻率較高的還是在C程式碼中。typedef與#define有些相似,但更多的是不同,特別是在一些複雜的用法上,就完全不同了,看了網上一些C/C++的學習者的部落格,其中有一篇關於typedef的總結還是很不錯,由於總結的很好,我就不加修改的引用過來了,以下是引用的內容(紅色部分是我自己寫的內容

)。

用途一:

定義一種型別的別名,而不只是簡單的巨集替換。可以用作同時宣告指標型的多個物件。比如:

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

// 和一個字元變數;

以下則可行:

typedef char* PCHAR;

PCHAR pa, pb;  

這種用法很有用,特別是char* pa, pb的定義,初學者往往認為是定義了兩個字元型指標,其實不是,而用typedef char* PCHAR就不會出現這樣的問題,減少了錯誤的發生。

用途二:
用在舊的C程式碼中,幫助struct。以前的程式碼中,宣告struct新物件時,必須要帶上
struct,即形式為: struct 結構名物件名,如:

struct tagPOINT1

 {
    int x;

    int y;
};

struct tagPOINT1 p1;

而在C++中,則可以直接寫:結構名物件名,即:tagPOINT1 p1;

typedef struct tagPOINT
{
    int x;

    int y;
}POINT;

POINT p1; // 這樣就比原來的方式少寫了一個struct,比較省事,尤其在大量使用的時

候,或許,在C++中,typedef的這種用途二不是很大,但是理解了它,對掌握以前的舊代

碼還是有幫助的,畢竟我們在專案中有可能會遇到較早些年代遺留下來的程式碼。

用途三:

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

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

typedef long double REAL;

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

typedef double REAL;

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

typedef float REAL;

也就是說,當跨平臺時,只要改下 typedef 本身就行,不用對其他原始碼做任何修改。

標準庫就廣泛使用了這個技巧,比如size_t。另外,因為typedef是定義了一種型別的新別名,不是簡單的字串替換,所以它比巨集來得穩健。
     這個優點在我們寫程式碼的過程中可以減少不少程式碼量哦!

用途四:

為複雜的宣告定義一個新的簡單的別名。方法是:在原來的聲明裡逐步用別名替換一部

分複雜宣告,如此迴圈,把帶變數名的部分留到最後替換,得到的就是原宣告的最簡化

版。舉例: 

 原宣告:void (*b[10]) (void (*)());

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

typedef void (*pFunParam)();

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

typedef void (*pFunx)(pFunParam);

原宣告的最簡化版:

pFunx b[10];
 
原宣告:doube(*)() (*e)[9];

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

typedef double(*pFuny)();

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

typedef pFuny (*pFunParamy)[9];

原宣告的最簡化版:

pFunParamy e;

理解複雜宣告可用的“右左法則”:從變數名看起,先往右,再往左,碰到一個圓括號

就調轉閱讀的方向;括號內分析完就跳出括號,還是按先右後左的順序,如此迴圈,直

到整個宣告分析完。舉例:

int (*func)(int *p);

首先找到變數名func,外面有一對圓括號,而且左邊是一個*號,這說明func是一個指標

;然後跳出這個圓括號,先看右邊,又遇到圓括號,這說明(*func)是一個函式,所以

func是一個指向這類函式的指標,即函式指標,這類函式具有int*型別的形參,返回值

型別是int。

int (*func[5])(int *);

func右邊是一個[]運算子,說明func是具有5個元素的陣列;func的左邊有一個*,說明

func的元素是指標(注意這裡的*不是修飾func,而是修飾func[5]的,原因是[]運算子

優先順序比*高,func先跟[]結合)。跳出這個括號,看右邊,又遇到圓括號,說明func數

組的元素是函式型別的指標,它指向的函式具有int*型別的形參,返回值型別為int。

這種用法是比較複雜的,出現的頻率也不少,往往在看到這樣的用法卻不能理解,相信以上的解釋能有所幫助。

*****以上為參考部分,以下為本人領悟部分*****

使用示例:

1.比較一:

#include <iostream>

using namespace std;

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

int ss(char a, char b)
{
    cout<<"功能1"<<endl;

    cout<<a<<endl;

    cout<<b<<endl;

    return 0;
}
 
int bb(char a, char b)
{

    cout<<"功能2"<<endl;

    cout<<b<<endl;

    cout<<a<<endl;

    return 0;

}

void main()
{

    A a;

    a = ss;

    a('a','b');

    a = bb;

    a('a', 'b');
}

2.比較二:

typedef int (A) (char, char);

void main()
{

    A *a;

    a = ss;

    a('a','b');

    a = bb;

    a('a','b');
}
 

兩個程式的結果都一樣:

功能1

a

b

功能2

b

a

*****以下是參考部分*****

參考自:http://blog.hc360.com/portal/personShowArticle.do?articleId=57527

typedef 與 #define的區別:

案例一:

通常講,typedef要比#define要好,特別是在有指標的場合。請看例子:

typedef char *pStr1;

#define pStr2 char *;

pStr1 s1, s2;

pStr2 s3, s4;
在上述的變數定義中,s1、s2、s3都被定義為char *,而s4則定義成了char,不是我們

所預期的指標變數,根本原因就在於#define只是簡單的字串替換而typedef則是為一

個型別起新名字。

案例二:

下面的程式碼中編譯器會報一個錯誤,你知道是哪個語句錯了嗎?

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++錯誤。雖然作者在這裡已經解釋得很清楚了,可我在這個地方仍然還是糊塗的,真的希望哪位高手能幫忙指點一下,特別是這一句“只不過此處變數p2的資料型別是我們自己定義的而不是系統固有型別而已”,難道自己定義的型別前面用const修飾後,就不能執行更改運算,而系統定義的型別卻可以?

分析:const char *p1 = string; 你可以這樣理解:(const char) *p1 = string, p1是一個指標,指向const char的東西,這個東西就是string(string是一個字元陣列的首地址,它的地址聲明後肯定是const的,除非該陣列銷燬),但是p1是一個指標變數,它是可以遞增的,即你看到的p1++,它可以完成從陣列的來遍歷陣列的目的。


而const pStr p2 = string;是這樣的:由於p2不是指標,const直接修飾到了p2,即現在的p2是常量了,它的型別是pStr(我們自己定義的型別),相當於const int p2, const long p2等等,const都是直接修飾p2的,只不過int,long是系統型別,而pStr是我們定義的型別。為什麼會出現這種效果了,就是因為typedef,它把char *定義成一個複合的型別,要從整體上來理解語義,而不是字元替換後來理解語義。

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 ",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++錯誤。

(注:關於const的限定內容問題,在本系列第二篇有詳細講解)。

  #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加在該語句的開頭就行了。

  (注:如果你對有些變數的宣告語法感到難以理解,請參閱本系列第十篇的相關內容)。
>1:int *(*a[5])(int, char*);
//pFun是我們建的一個類型別名
typedef int *(*pFun)(int, char*);
//使用定義的新型別來宣告物件,等價於int* (*a[5])(int, char*);
pFun a[5];

>2:void (*b[10]) (void (*)());
//首先為上面表示式藍色部分宣告一個新型別
typedef void (*pFunParam)();
//整體宣告一個新型別
typedef void (*pFun)(pFunParam);
//使用定義的新型別來宣告物件,等價於void (*b[10]) (void (*)());
pFun b[10];

>3. doube(*)() (*pa)[9];
//首先為上面表示式藍色部分宣告一個新型別
typedef double(*pFun)();
//整體宣告一個新型別
typedef pFun (*pFunParam)[9];
//使用定義的新型別來宣告物件,等價於doube(*)() (*pa)[9];
pFunParam pa;

下面的3篇文件是我在學習typedef歸納的總結,樓主覺得有用一定要給分哦!

//第1篇:typedef語句格式
#include<stdio.h>
typedef int GTYPE;//定義全域性型別GTYPE
void main()
{
  //一、基本格式
  typedef char CH;//重定義基本型別
  typedef struct{int a,b,c;}STRU;//重定義自定義型別(結構、共用、列舉)
  typedef union{int a,b,c;}UNIO;
  typedef enum{one,two,three}NUM;
  typedef char* STR;//重定義派生型別(指標、陣列、函式)
  typedef int AI[10];
  typedef void FUN(void);
  //可見,typedef使用的格式為:typedef 變數/函式定義語句;即在原變數/函式定義語句的開頭加上
  //typedef,便可將原變數/函式定義語句改寫為型別定義語句,原來定義的變數名/函式名都成了型別名。
  //注:當重定義基本、自定義型別時,格式也可總結為:typedef 原型別 新型別1,新型別2,…;

  //二、觀察原型別
  //1.原型別可以帶有型別限定符
  typedef  const int CI;//原型別含const限定符
  CI ci=3;
  //ci=4;//可見CI確實代表const int
 
  //2.原型別可以是typedef定義的新型別
  typedef CH NEWCH;//CH已在前面定義為char
  NEWCH nc='a';
 
  //3.原型別不可帶有儲存類別
  //typedef static int SI;//錯誤,"指定的儲存類多於1個"
  //typedef register int RI;//錯誤同上
 
  //4.原型別應是一種型別,而不可是變數/物件
  float f=0;//將f定義為變數
  //typedef f FL;//錯誤
 
  //5.不宜重定義的型別
  typedef const CON;//重定義const
  //CON int a=0;//但該型別無法正常使用
  typedef unsigned US;//重定義unsigned
  US us1=0;//正確,相當於unsigned int
  //US int us2;//錯誤,無法正常使用
  //注:因const、unsigned等並不是一種獨立的型別,故不便對它們重定義

  //三、觀察新型別
  //1.新型別的作用域
  typedef int LTYPE;//定義區域性型別LTYPE
  void fun();//觀察LTYPE在fun中是否有效
  fun();
  //可見,用typedef定義的型別也有作用域之分。在函式內用typedef定義的是區域性型別
  //typedef也可以像變數定義語句一樣放置在函式之外,這時定義的是全域性型別
 
  //2.新型別可否是已有型別
  //typedef int float;//錯誤,不能重定義標準型別
  typedef int TYPE; //定義了新型別TYPE
  //typedef char TYPE;//錯誤,"TYPE重定義"
  typedef int GTYPE;//正確,儘管GTYPE是已有型別,但它是本函式外定義的
  //可見,新型別名必須是合法的識別符號,它在其作用域內必須是唯一的

  //3.新型別可否不止一個
  typedef float LENGTH,WIDTH;//對float取兩個別名,LENGTH和WIDTH
  LENGTH  L=0;//用新型別定義變數
  WIDTH W=0;
  //可見,可以一次性為某個原型別指定多個別名

  //4.一次可否定義多個不同型別
  //typedef int I,float F;//一次定義了二個不同型別I和F
  //F v=6.0;//試圖使用F型別
  //printf("v=%f ",v);//v=0,型別F無效
  //可見,一條typedef語句只宜為一個原型別定義別名

}
void fun()
{ //LTYPE i;//錯誤,"LTYPE:未定義的識別符號"
  GTYPE g=1;//使用全域性型別GTYPE
  printf("g=%d ",g);//正常使用
}

//第3篇:typedef具體應用

#include<stdio.h>
int fun1(int a,int b){return 0;}
int fun2(int a,int b){return 0;}
int fun3(int a,int b){return 0;}
void main()
{ //一、使用typedef的優點
  //1可以為現有型別取一個更有意義的名字,增加程式的可讀性,如
  typedef char* STRING;
  STRING s1="string1",s2="string2";//STRING便可當作字串型別來使用
 
  //2使變數定義更簡短,減少書寫麻煩,如
  typedef float A3[2][3][4];//為2*3*4的實型陣列取簡短的名字A3
  A3 a,b,c;//相當於定義float a[2][3][4],b[2][3][4],c[2][3][4]
  typedef unsigned int(*PFUN)(int(*)[4]);//PFUN是一種函式指標,該類函式引數為一維陣列型指標,返回值為無符號整型
  PFUN pf1,pf2;//相當於定義unsigned int(*pf1)(int(*)[4]);unsigned int(*pf2)(int(*)[4])
 
  //3在定義複雜型別前,先用typedef建立一些中間型別,再用中間型別去構造複雜型別,以降低其複雜性(主要用途)
  //例:對於如下的複雜定義
  int(*ap[3])(int,int);//ap是一個數組,其元素為函式型指標,該類函式的引數和返回值都是整型
  //採用typedef以降低定義難度
  //方法1
  typedef int(*PF)(int,int);//定義PF為該種函式的指標
  PF ap1[3];//ap1為一陣列,每個元素都是PF型別
  ap1[0]=fun1;ap1[1]=fun2;ap1[2]=fun3;
  //方法2
  typedef int FUN(int,int);//將該種函式定義為FUN型別
  FUN* ap2[3];//ap2為一陣列,每個元素都是指向FUN的指標
  ap2[0]=fun1;ap2[1]=fun2;ap2[2]=fun3;
 
  //4增加程式的可移植性(有利於程式在不同處理器、作業系統和編譯系統之間的移植)
  /*例如,在TC下讀檔案的程式段如下:
  FILE* fp;
  long buffer1;
  fread(&buffer1,sizeof(long),1,fp);//每次讀出4個位元組
  若在VC下每次需要讀出8個位元組,程式需如下修改:
  double buffer2;
  fread(&buffer2,sizeof(double),1,fp);
  現用typedef方法,程式段如下:
  typedef long UNIT;//UNIT在TC中代表long,在VC中代表double
  UNIT buffer;
  fread(&buffer,sizeof(UNIT),1,fp);//每次讀出UNIT個位元組
  當移植到VC下時,只需改動UNIT的定義即可:typedef double UNIT;*/

  //二、typedef與define的區別
  //用define也可實現簡單的型別替換,如
  #define INT long //用INT來代替long
  //兩種方式的區別如下:
  //1.二者處理時間不同,巨集替換是在預編譯時進行的,而型別定義是在正式編譯時處理的
  //2二者本質不同,巨集替換隻是將巨集名簡單替換為目標字串,而型別定義如同定義變數一樣
  //是真的為程式增加了一種可用型別
  //3.二者複雜性不同,用typedef可定義各種複雜的型別,並以各種方式使用新型別(詳見10_10_2.cpp)
  //而define只能替換基本型別和自定義型別,無法替換派生型別,且使用起來很不安全,例如
  #define pi int* //試圖用pi代替整型指標
  pi pi1;//正確,展開後為int* pi1;
  pi pi2,pi3;//錯誤,原意是將pi2,pi3都定義成整型指標,但展開後為int* pi2,pi3; pi3並未定義成指標

  #define NUM enum{one,two,three}//試圖用NUM代替該列舉型別
  NUM n1;//正確,定義了列舉常量one,two,three和列舉變數n1
  //NUM n2;//錯誤,展開後為enum{one,two,three}n2;從而造成列舉常量one,two,three的重定義

  #define DATE struct{int y,m,d;}
  DATE *pd;//正確,定義了該結構型指標
  //pd=(DATE)1;//錯誤,展開後為pi=(struct{int y,m,d;})1;目前尚不支援此種類型轉換寫法

  #define TIME union{int h,m,s;}
  //int L=sizeof(TIME);//錯誤,展開後為int L=sizeof(union{int h,m,s;});sizeof運算元錯誤
  //可見,用define進行型別替換時,會產生各種意想不到的錯誤,故應避免使用,而改用安全的typedef

}
//第2篇:typedef詳細使用

/* 為了從易到難地使用typedef,現將C++資料型別按照型別名的來源和複雜性重分類如下:
一、基本型別(型別名是系統指定的單一識別符號)
in,char,float,double,void,const
二、自定義型別(型別名是使用者定義的單一識別符號)
1.結構型別
struct stru{int i;struct stru*;};
2.共用型別
union unio{int i;enum num[10];};
3.列舉型別
enum num{a,b,c};
4.typedef型別
typedef double db;
三、派生型別(型別名由已有型別與其它符號組合而成)
1.指標型別(由 已有型別* 組成)
void*,char**,void(*)(),struct stru*,enum num*
2.陣列型別(由 已有型別[][] 組成)
int[3][4],struct stru[10],enum num[10],char*[10]
3.函式型別(型別名是各種符號組成的函式原型)
void main(void),char* strcpy(char*,char*)

以上三大類別的型別識別符號由簡單到複雜,學習typedef時要依照先後順序,練習每種型別的重定義
每定義出一種新型別後,從以下幾個方面使用它:用它定義變數、指標、陣列、帶儲存類別的物件、
帶const的物件;用它作函式引數和返回值型別;用它進行型別轉換;用sizeof求長度*/

#include<stdio.h>
#include<stdlib.h>
void main()
{
  //一、重定義基本型別
  //1.定義
  typedef int in;
  typedef char ch;
  typedef float fl;
  typedef double db;
  typedef unsigned int ui;//或寫為typedef unsigned ui;
  typedef unsigned char uc;
  typedef void vo;

  //2.使用
  in i=3;printf("i=%d
",i);  //用新型別定義變數
  ch* pc="pc";printf("pc=%s
",pc);//用新型別定義指標
  fl af[3]={1,2,3};printf("af[0]=%f
",af[0]);//用新型別定義陣列
  static double sd;printf("sd=%f
",sd);//用新型別定義帶儲存類別的變數
  const ui cui=3;printf("cui=%d
",cui);//用新型別定義帶型別限定符的變數
  vo fun1(uc);fun1('a');//用新型別作函式的引數和返回值型別
  printf("in(1.5)=%d
",in(1.5));//將新型別用作型別轉換
  printf("$db=%d

",sizeof(db));//對新型別求長度,$db=$double=8

  //二、重定義自定義型別
  //1.定義
  //(1)完整寫法(先定義好自定義型別,再重定義它)
  struct datetype                 //重定義結構型別
  { ui year;//ui即是unsigned int;
    uc month;//uc即是unsigned char;
    uc day;};
  typedef datetype date;
  union scoretype                 //重定義共用型別
  { fl sco1;
    ch* sco2;};
  typedef scoretype score;
  enum numtype{one,two,three};    //重定義列舉型別
  typedef numtype num;
  typedef num newnum;             //重定義typedef型別

  //(2)通常寫法(對無型別名的結構、共用、列舉定義語句直接重定義)
  typedef struct{uc hou,min,sec;} time;
  typedef union{fl sco1;ch* sco2;} sco;
  typedef enum{red,green,blue} color;
  //可見,無論何種寫法,都遵循typedef語句的格式:typedef 原型別  新型別;

  //2.使用
  date vs={2002,10,15};//定義結構變數
  printf("vs:%d.%d.%d
",vs.year,vs.month,vs.day);
  score vu,*pu=&vu;printf("&vu=%x pu=%x
",&vu,pu);//定義共用指標
  newnum ae[3]={one,two,three};//定義列舉陣列
  printf("ae[0]=%d ae[1]=%d ae[2]=%d
",ae[0],ae[1],ae[2]);
  static num vn;//定義帶儲存類別的物件
  printf("vn=%d
",vn);
  const sco cs={90};//定義const物件
  printf("cs.sco1=%f
",cs.sco1);
  time fun2(time);//作函式的引數和返回值型別
  static time vt=fun2(vt);
  printf("fun2:%d:%d:%d
",vt.hou,vt.min,vt.sec);
  vn=(num)10;printf("vn=%d
",vn);//將新型別用作型別轉換
  printf("$time=%d
",sizeof(time));//對新型別求長度,$time=3
  printf("$sco=%d
",sizeof(sco));//對新型別求長度,$sco=4
  printf("$color=%d

",sizeof(color));//對新型別求長度,$color=4

//未完待續
  //2.重定義陣列型別
  //(1)定義
  typedef int ai[5];//重定義基本型別陣列
  typedef float a2[3][4];//重定義陣列型陣列(二維陣列)
  typedef time as[2];//重定義結構型陣列
  typedef sco au[2];//重定義共用型陣列
  typedef color ae2[2];//重定義列舉型陣列
  typedef char* ap2[2];//重定義指標型陣列
  //注:重定義陣列時,C++暫不支援typedef int[10] ai這種正規寫法

  //(2)使用
  //定義變數(陣列變數)
  ai vai={1,2,3,4,5};//相當於int vai[5];
  printf("vai:%d %d
",vai[0],vai[1]);
  //定義指標(陣列指標)
  a2 va2,*pa2=&va2;//相當於float va2[3][4],(*pa2)[3][4]=&va2;
  printf("&va2=%x pa2=%x
",va2,pa2);
  //定義陣列(多維陣列)
  as vas[2]={{10,20,30},{20,40,59}};//vas此時是二維結構陣列,相當於time vas[2][2];
  printf("vas[0][0]:%d:%d:%d
",vas[0][0].hou,vas[0][0].min,vas[0][0].sec);
  //定義帶儲存類別的陣列
  static au vau;//vau是含2元素的靜態共用陣列
  printf("vau[2]:%f %f
",vau[0].sco1,vau[1].sco1);
  //定義cosnt陣列
  const ae2 cae2={red,green};//cae2是含2元素的常量列舉陣列
  printf("cae2[2]:%d %d
",cae2[0],cae2[1]);
  //定義函式
  void fun4(ap2);//引數是指標陣列,相當於void fun4(char* ap2[2]);
  ap2 vap2={"str1","str2"};//vap2的兩元素分別指向"str1"和"str2"
  fun4(vap2);
  printf("$ai=%d
",sizeof(ai));//對新型別求長度,$ai=$int[5]=20
  printf("$a2=%d
",sizeof(a2));//對新型別求長度,$a2=$float[3][4]=48
  printf("$as=%d
",sizeof(as));//對新型別求長度,$as=$=time[2]=6
  printf("$au=%d
",sizeof(au));//對新型別求長度,$au=$=sco[2]=8
  printf("$ae2=%d
",sizeof(ae2));//對新型別求長度,$ae2=$color[2]=8
  printf("$ap2=%d

",sizeof(ap2));//對新型別求長度,$ap2=$=char*[2]=8
  //注:因函式返回值不能是陣列型別,故這裡不再將重定義型別用作函式返回值型別

  //3.重定義函式型別
  //(1)定義方法
  int fun(int,int);//聲明瞭一個函式,它有兩個整型引數、返回值為整型
  typedef int FUN(int,int);//定義了一個型別,它代表含兩個整型引數、返回值為整型的函式

  //(2)使用方法
  //定義變數(函式)
  FUN max,min;//用型別FUN定義了兩個物件max和min,因FUN是函式型別,故它們自然是函式物件
  //這裡相當於int max(int,int)和int min(int,int)兩個宣告
  printf("max(3,5)=%d
",max(3,5));//呼叫這兩個函式
  printf("min(3,5)=%d
",min(3,5));
  //定義指標(函式指標)
  FUN* pfun;//pfun是一種指標,它專門指向FUN型別的物件。相當於定義int(*pfun)(int,int);
  pfun=max;//max和min都是FUN型別的物件
  printf("&max=%x pfun=%x
",max,pfun);
  //其它使用基本無意義,略

  //可見,若程式中要用到許多型別相同但名稱不同的函式,可先用typedef抽象出型別,再用該型別
  //一次便可宣告許多函式,以簡化函式原型的書寫。但這種型別只能用來宣告函式,而不能用來定義函式

} //main end

//下面是main中用到的函式
void fun1(unsigned char p)//因vo與uc的作用域不在此處,故這裡直接使用void和unsigned 
{ printf("fun1:%c
",p);//這樣與函式原型vo fun1(uc)亦對應
}

typedef struct{unsigned char hou,min,sec;} time;//因main中的time型別作用域已結束,故這裡要使用必須重新定義
time fun2(time p)
{ p.hou=20;p.min=30,p.sec=55; 
  return p;}
/*注:嚴格來說這裡的time與main中函式原型裡的time並不是同一型別,但系統忽略了這點
正確用法應將main中的time作為全域性型別定義在程式開頭,這樣其後的函式都可共享使用該型別*/

void* fun3(float** p)
{ printf("fun3:malloc(%d)",p); 
  return malloc(int(p));
}

typedef char* ap2[2];
void fun4(ap2 p)
{ printf("fun4:p[0]=%s p[1]=%s
",p[0],p[1]);
}

int max(int a,int b)
{ return a>b?a:b;
}

int min(int a,int b)
{ return a<b?a:b;
}