C/C++ typedef详解

关于typedef,就是给某个类型起一个别名。并没有新类型的出现。但绝不仅仅是文本替换啊!!!

1.包含指针的用法

[cpp]  view plain copy
  1. #include <stdio.h>  
  2.   
  3. typedef charPCHAR;  
  4.   
  5. int main()  
  6. {  
  7.     PCHAR first_name = "first name";  
  8.     PCHAR last_name = "last name";  
  9.   
  10.     printf("--%s-- \n",first_name);  
  11.     printf("--%s-- \n",last_name);  
  12. }  

2.包含结构体的用法

[cpp]  view plain copy
  1. typedef struct Student  
  2. {  
  3.     int age;  
  4.     int id;  
  5.     char* name;  
  6. }STU;  
  7.   
  8. STU student;  

或者:

[cpp]  view plain copy
  1. struct Student  
  2. {  
  3.     int age;  
  4.     int id;  
  5.     char* name;  
  6. };  
  7.   
  8. typedef struct Student student;  

3.下面分析几个例子。以下几个例子来自互联网,但都只是浅尝辄止。大家苦于没有实际的例子与之对应,现在我们做个分析!

3.1 有这样一个东西 : char *  (*f[5]) (int,char*);

网上都说左右法则,其实也就一个人说,其余都是copy的。我的理解与此相类似。我主要是看括号来分析,下面我们来分析一下。


我们可以看出,主要是由2个括号组成的。第一个括号里是*f[5],第二个括号里是 int,char*。最前面还有一个char*.


 --->在*f[5]中,由于[]的优先级高于*,所以,他是一个数组,且是一个指针数组。加上外面的括号,我们可以得出,他是一个函数指针数组。

---->那么,(int ,char*)就是每个函数的参数。


上面我们提到  !每个函数  !,因为函数指针数组里,每个元素都指向一个函数,对我们的声明来说,有5个参数是(int ,char*)的函数。


那么char* 就是他们的返回值。

所以我们知道,要重新起名的,是函数指针数组的名字,就是那个f。

所以我们用typedef来声明它的时候,需要这样作就好了:


typedef char* (*array_func[5])(int a,char* b);//因为是声明,所以参数里的a,b不填也是可以的。
array_func functions;//那么functions就有5个元素
当然,这是大家在网上都能看见的,但是这样是唯一的吗?答案是否的。
你可以
typedef char* (*array_func)(int a,char* b);
那么声明变量的时候,你就用
array_func functions[5];
效果是一样的,大家灵活对待。


好了,说了这么多,让我们来看一下具体的应用。


[cpp]  view plain copy
  1. #include <stdio.h>  
  2.   
  3. //char *(*real_func[5])(int,char*);  
  4.   
  5. char* get_info(int id,char* name);  
  6.   
  7. typedef char* (*array_func[5])(int a,char* b);   
  8.   
  9. array_func func;  
  10.   
  11. int main()  
  12. {  
  13.     func[0] = get_info;  
  14.   
  15.     printf("--%s--",func[0](1,"your name"));  
  16. }  
  17.   
  18.   
  19. char* get_info(int id,char* name)  
  20. {  
  21.     return name;  
  22. }  


代码是可以运行的,大家可以尝试一下,我的是win下的cygwin linux环境,用linux的话,qq不在线,老婆以为我丢了。。。


如果写成

[cpp]  view plain copy
  1. typedef char* (*array_func)(int a,char* b);  
[cpp]  view plain copy
  1. array_func functions[5];  
[cpp]  view plain copy
  1.   
可能更好理解一些。

仔细观察

[cpp]  view plain copy
  1. typedef char* (*array_func[5])(int a,char* b);  

[cpp]  view plain copy
  1. char* get_info(int id,char* name);  

是一样的类型,所以能将get_info赋值给functions[0]。因为functions数组中每个元素都是get_info类型的函数指针。


3.2再来分析一个

有这样一个定义  : void (*b) (void (*) ()) ;


老规矩,先看括号。左面括号里是 *b ,右面括号里是void(*)().最左面还有一个void

很明显啊,这就是一个函数指针嘛,只不过参数复杂了点。

void(*)()!大家一看 ,很眼熟吗,这不又是一个函数指针吗,只不过忘记起名字而已,无伤大雅。

我们稍微给他起个名字,

void (*b) (void (*c) ()) ;

那么是不是可以给他typedef了呢,先别急,这里面是不是有2个名字呢?对啊,那样编译器是不是会傻傻分不清呢?

那就不知道啦,只是小编我是傻傻分不清了。。。

这里姑且给他拆分成2段,实际上同时有函数指针和其他结构并存,我们一般要分步声明!!!

typedef现在就是起个新名字嘛。


typedef void (*param)();

typedef void (*function)(param);

这样的好处大家一会就见到了。现在让我们看代码!


[cpp]  view plain copy
  1. #include <stdio.h>  
  2.   
  3. //void (*b) (void (*) ()) ;  
  4.   
  5. typedef void (*param)();  
  6.   
  7. typedef void (*function)(param);  
  8.   
  9.   
  10. void my_func(param p);//我们分步声明了,可以直接使用param来做参数  
  11. void fff();  
  12.   
  13. int main()  
  14. {  
  15.     function f;  
  16.     param p;  
  17.   
  18.     p = fff;  
  19.     f = my_func;  
  20.     f(p);  
  21. }  
  22.   
  23. void my_func(param p)  
  24. {  
  25.     p();  
  26. }  
  27.   
  28. void fff()  
  29. {  
  30.     printf("---- \n\n");  
  31. }  

大家可以运行一下代码。

对程序猿来说看代码才是王道,看我写的汉字简直就是瞎扯淡啊!!!

3.3现在看最后一个。

有这么个定义  : doube(*)()   (*e)[9]; ---》类似char x;的造型。

相信大家很快就看出来了啊,  doube(*)() 是一个函数指针,(*e)[9]是一个数组指针,就是个指针!!!步长是9.  这个指针的类型是 函数指针。

有点绕,大家好好梳理一下。

那么按照我们上个例子的总结,要分开声明一下。

typedef double (*func)();
typedef func(*array)[9];

那么声明 :array func_array ;

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3.   
  4. //doube(*)()   (*e)[9];  
  5.   
  6. typedef double (*func)();  
  7. typedef func(*array)[9];  
  8.   
  9. array func_array ;  
  10.   
  11. double get_number() ;  
  12.   
  13. int main()  
  14. {  
  15.     func_array = (array)malloc(sizeof(array));//切记,指针使用时要申请内存  
  16.     (*func_array)[0] = get_number;  
  17.     printf("%f \n\n\n",(*func_array)[0]());  
  18. }  
  19.   
  20. double get_number()   
  21. {  
  22.     return 1000000;  
  23. }  

-----------------------------------------------------------------------------------------------
看看下面的程序:
define INTEGER int

typedef int INTEGER;
乍一看,也对,除了顺序上的差别
再来看看下面的程序
typedef int INT20[20];
这个用define解释就有些牵强了,不过还是可以一定程度上解释的,比如:
INT20 a;
那么,a就是一个大小为20的整型数组,类似于int a[20];
于是,我们就可以认为:typedef就是define了
直到你遇到如下的程序为止:
typedef int (* pFun)();
define理论立刻崩溃了。下面我讲一下理解typedef的正确方法,分为以下几步:
1.确定被声明的类型:遇到typedef时,从左到右进行扫描,找到第一个“陌生”的标志符,这个标志符就应该是语句所声明的类型名称。eg:
typedef int* (* pt)(int* (*pn)(int * p1,int *p2),int * p3);
如果pt是生词(既不是保留字,也不是生命过的类型),那么它就是要声明的类型。其它的名字都是为了阅读方便的占位符,可有可无。也就是说,上面的语句等价为:
typedef int * (*pt)(int * (*)(int *, int *),int *);
2,之后一旦遇到该类型声明的变量,则在该类型的typedef式中用变量代替类型,去掉typedef关键字,所得到的声明式等价与原来的声明。eg:
pt p;
这个声明式,可以经过两步变化为等价的声明式。
首先,回到pt的typedef式:
typedef int * (*pt)(int * (*)(int *, int *),int *);
用p代替pt:
typedef int * (*p)(int * (*)(int *, int *),int *);
然后把typedef去掉,得到:
int * (*p)(int * (*)(int *, int *),int *);
这个语句与 pt p;
意义相同。

如果你基础扎实,应该知道,这是个函数指针的声明,所指向的函数有两个int * 参数,第一个参数的 (*)(int *,int *)的这个部分又是一个指向函数的指针,这个函数返回一个指向函数指针,经过(*)处理后,变成一个变量,再经过int *处理后,又变成一个指向整形变量的指针 int *。第二个参数是int * ,整个函数返回一个int *。
 
个人认识:在对待typedef定义的类型时要把整个定义看做一个整体 如上面的例子
typedef int INT20[20]; 即定义了一个有二十个元素的整型数组的别名,以后就可以用 INT20 来定义有二十个元素的整型数组变量了
几个经典的实例,以供参考
>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; 

不管实在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修饰后,就不能执行更改运算,而系统定义的类型却可以?

 

你可能感兴趣的:(C++,c,typedef,typedef,typedef)