原创 typedef的基本用法

2011-4-4 13:55 1652 10 10 分类: 工程师职场

类型重定义typedef

在现实生活中,信息的概念可能是长度,数量和面积等。在C语言中,信息被抽象为int、float和double等基本数据类型。从基本数据类型名称上,不能够看出其所代表的物理属性,并且int、float和double为系统关键字,不可以修改。为了解决用户自定义数据类型名称的需求,C语言中引入类型重定义语句typedef,可以为数据类型定义新的类型名称,从而丰富数据类型所包含的属性信息。
typedef的语法描述
typedef 类型名称 类型标识符;
typedef为系统保留字,“类型名称”为已知数据类型名称,包括基本数据类型和用户自定义数据类型,“类型标识符”为新的类型名称。例如:
typedef double LENGTH;
typedef unsigned int COUNT;
定义新的类型名称之后,可像基本数据类型那样定义变量。例如:
typedef unsigned int COUNT;
unsigned int b;
COUNT c;
typedef 的主要应用有如下的几种形式:
1) 为基本数据类型定义新的类型名。例如:
typedef unsigned int COUNT;
typedef double AREA;
此种应用的主要目的,首先是丰富数据类型中包含的属性信息,其次是为了系统移植的需要,稍后详细描述。
2) 为自定义数据类型(结构体、公用体和枚举类型)定义简洁的类型名称。例如:
struct Point
{
double x;
double y;
double z;
};
struct Point oPoint1={100,100,0};
struct Point oPoint2;
其中结构体struct Point为新的数据类型,在定义变量的时候均要有保留字struct,而不能像int和double那样直接使用Point来定义变量。如果经过如下的修改,
typedef struct tagPoint
{
double x;
double y;
double z;
} Point;
定义变量的方法可以简化为
Point oPoint;
由于定义结构体类型有多种形式,因此可以修改如下:
typedef struct
{
double x;
double y;
double z;
} Point;
3) 为数组定义简洁的类型名称。例如,定义三个长度为5的整型数组,
int a[10],b[10],c[10],d[10];
在C语言中,可以将长度为10的整型数组看作为一个新的数据类型,再利用typedef为其重定义一个新的名称,可以更加简洁形式定义此种类型的变量,具体的处理方式如下:
typedef int INT_ARRAY_10[10];
typedef int INT_ARRAY_20[20];
INT_ARRAY_10 a,b,c,d;
INT_ARRAY_20 e;
其中INT_ARRAY_10和INT_ARRAY_20为新的类型名,10 和20 为数组的长度。a,b,c,d均是长度为10的整型数组,e是长度为20的整型数组。
4) 为指针定义简洁的名称。首先为数据指针定义新的名称,例如
typedef char * STRING;
STRING csName={“Jhon”};
其次,可以为函数指针定义新的名称,例如
typedef int (*MyFUN)(int a,int b);
其中MyFUN代表 int *XFunction(int a,intb)类型指针的新名称。例如
typedef int (*MyFUN)(int a,int b);
int Max(int a,int b);
MyFUN *pMyFun;
pMyFun= Max;
在使用typedef时,应当注意如下的问题:
1) typedef的目的是为已知数据类型增加一个新的名称。因此并没有引入新的数据类型。
2) typedef 只适于类型名称定义,不适合变量的定义。
3) typedef 与#define具有相似的之处,但是实质不同。
提示 #define AREA double 与 typedef double AREA 可以达到相同的效果。但是其实质不同, #define为预编译处理命令,主要定义常量,此常量可以为任何的字符及其组合,在编译之前,将此常量出现的所有位置,用其代表的字符或字符组合无条件的替换,然后进行编译。typedef是为已知数据类型增加一个新名称,其原理与使用int double等保留字一致。

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

MFC源代码:

#ifdef STRICT
typedef void *HANDLE;
#define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name
#else
typedef PVOID HANDLE;
#define DECLARE_HANDLE(name) typedef HANDLE name
#endif

DECLARE_HANDLE(HMODULE);
DECLARE_HANDLE(HINSTANCE);
DECLARE_HANDLE(HLOCAL);
DECLARE_HANDLE(HGLOBAL);
DECLARE_HANDLE(HDC);
DECLARE_HANDLE(HRGN);
DECLARE_HANDLE(HWND);
DECLARE_HANDLE(HMENU);
DECLARE_HANDLE(HACCEL);
DECLARE_HANDLE(HTASK);


理解:
    HANDLE就是PVOID,也就是无类型指针,
    上面这些资源的句柄Handles都不过是指向struct的指针,至于这个struct的用处,连M$都说unused了,现在解释下M$这么做的意义,这就是所谓数据封装,你可以在你的程序中把M$的内部结构指针传来传去,可是你却不知道它到底指向的内容是什么。

    句柄与指针确实是完全不同的两个概念。句柄仅仅是一个32位整数,WIN32中用于标记某个系统或进程的对象,可以理解为对象索引(由于M$未完全公开相关技术,在一定程度上只能如此理解),这个索引更像是一种映射关系(从句柄到对象指针的映射),而不是纯粹意义上的“数组下标”。

     句柄可以理解为用于指向或标识内存的一块“资源”,这些资源如:文件(file)、内存块(block of memory)、菜单(menu)等等。操作系统通过句柄来定位核心对象和系统资源。
    指针即为指向内存的“数据或指令”某一单元。

    说的确切一点,句柄实际上是一种指向某种资源的指针,但与指针又有所不同:指针对应着一个数据在内存中的地址,得到了指针就可以自由地修改该数据。Windows并不希望一般程序修改其内部数据结构,因为这样太不安全。所以Windows给每个使用GlobalAlloc等函数声明的内存区域指定一个句柄(本质上仍是一个指针,但不要直接操作它),平时你只是在调用API函数时利用这个句柄来说明要操作哪段内存。

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

函数指针(function pointer)使用详解

函数指针(function pointer),是一个让人既爱又恨的东东,爱的是,它的确是很强大,用的好的话,能写出结构很清晰的代码,比较常见的就是一些事件处理模型中用到的处理事件的回调函数等。恨的是,函数指针的使用,是一个比较高级的话题,对一般的程序员来说,这个语法不容易掌握,用的时候容易出错,调试的时候还比较麻烦。C++中提出了虚函数(virtual function),有人说可以用虚函数功能来替代函数指针了,函数指针可以被抛弃了,但是,我们知道,使用虚函数有额外的开销的,因此,很多追求高效率的程序员还是更加钟爱函数指针。下面就函数指针的使用,进行举例一一说明:

1. 最普通的使用方式,函数指针指向普通函数的情况

#include <stdio.h>
 
typedef void (*Handle)(void *); 
 
void HandleChar(void * pArg)
{
    printf("%c\n", *(char *)pArg);
}
 
void HandleInt(void * pArg)
{
    printf("%d\n", *(int *)pArg);
}
 
void TestHandle(void * pArg, Handle pHandle)
{
    (*pHandle)(pArg);
}
 
int main()
{
    char bCh = 'I';
    TestHandle(&bCh, &HandleChar);
 
    int nNum = 100;
    TestHandle(&nNum, &HandleInt);
}

2. 函数指针指向静态类成员函数的情况

#include <stdio.h>
 
class Test
{
public:
 
    static void HandleChar(void * pArg)
    {   
        printf("%c\n", *(char *)(pArg));
    }   
 
    static void HandleInt(void * pArg)
    {   
        printf("%d\n", *(int *)(pArg));
    }   
};
 
typedef void (*Handle)(void *); 
 
void TestHandle(void * pArg, Handle pHandle)
{
    (*pHandle)(pArg);
}
 
int main()
{
    char bCh = 'I';
    TestHandle(&bCh, &Test::HandleChar);
 
    int nNum = 100;
    TestHandle(&nNum, &Test::HandleInt);
}

3.函数指针指向普通的类成员函数,在类成员函数中的调用的情况

#include <stdio.h>
 
class Test
{
public:
 
    typedef void (Test::*Handle)(void *); 
 
    void HandleChar(void * pArg)
    {   
        printf("%c\n", *(char *)(pArg));
    }   
 
    void HandleInt(void * pArg)
    {   
        printf("%d\n", *(int *)(pArg));
    }   
 
    void TestHandle(void * pArg, Handle pHandle)
    {   
        (this->*pHandle)(pArg);
    }   
};
 
int main()
{
    Test oTest;
    char bCh = 'I';
    oTest.TestHandle(&bCh, &Test::HandleChar);
 
    int nNum = 100;
    oTest.TestHandle(&nNum, &Test::HandleInt);
}

3.函数指针指向类成员函数,在全局函数中调用的情况。

#include <stdio.h>
 
class Test
{
public:
 
    void HandleChar(void * pArg)
    {   
        printf("%c\n", *(char *)(pArg));
    }   
 
    void HandleInt(void * pArg)
    {   
        printf("%d\n", *(int *)(pArg));
    }   
};
 
typedef void (Test::*Handle)(void *); 
 
void TestHandle(Test &obj, void * pArg, Handle pHandle)
{
    (obj.*pHandle)(pArg);
}
 
int main()
{
    Test oTest;
    char bCh = 'I';
    TestHandle(oTest, &bCh, &Test::HandleChar);
 
    int nNum = 100;
    TestHandle(oTest, &nNum, &Test::HandleInt);
}

通过上面的例子,以及结合我自己在写代码过程中一的一些经验,我觉得,在使用函数指针时,需要注意以下几点:
1. 在使用函数指针的时候,最好用typedef进行类型定义,这样语法看起会来会比较清晰,减少出错概率
2. 当类成员函数作为函数的函数指针型参数时,一定要加上类名做其作用域前缀,如上面的Test::HandleChar和Test::HandleInt
3. 当函数指针为在成员函数类型时,调用某个确定的函数时,一定要显式地指名其所在的域,如上面的(this->*pHandle)(pArg)和(obj.*pHandle)(pArg)

OK, that’s all, good luck!

http://www.wuzesheng.com/?p=348

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

 

文章评论0条评论)

登录后参与讨论
我要评论
0
10
关闭 站长推荐上一条 /2 下一条