当前位置: 首页 > >

C 中typedef 函数指针的使用

发布时间:

C 中typedef 函数指针的使用
2017年3月8日 12:53

C 中typedef 函数指针的使用

类型定义的语法可以归结为一句话:只要在变量定义前面加上typedef,就成了类型定义。这儿的原本应 该是变量的东西,就成为了类型。

int integer;

//整型变量

int *pointer; //整型指针变量

int array [5]; //整型数组变量

int *p_array [5]; //整型指针的数组的变量

int (*array_pointer) [5];//整型数组的指针的变量

int function (int param);//函数定义,也可将函数名看作函数的变量

int *function (int param);//仍然是函数,但返回值是整型指针

int (*function) (int param);//现在就是指向函数的指针了

若要定义相应类型,即为类型来起名字,就是下面的形式:

typedef int integer_t;

//整型类型

typedef int *pointer_t;

//整型指针类型

typedef int array_t [5]; //整型数组类型

typedef int *p_array_t [5]; //整型指针的数组的类型

typedef int (*array_pointer_t) [5]; //整型数组的指针的类型

typedef int function_t (int param);

//函数类型

typedef int *function_t (int param); //函数类型

typedef int (*function_t) (int param); //指向函数的指针的类型

注意:上面的函数类型在C中可能会出错,因为C中并没有函数类型,它的函数变量会自动退化成函数指 针;在C++中好像是可以的。在这里主要说明的是形式上的相似性.

typedef的一般形式为:

typedef 类型

定义名;

在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些 比较复杂的类型声明。

其实,在C语言中声明变量的时候,有个存储类型指示符(storage-class-specifier),它包括我们熟悉 的extern、static、auto、register。在不指定存储类型指示符的时候,编译器会根据约定自动取缺省 值。另外,存储类型指示符的位置也是任意的(但要求在变量名和指针*之前),也就是说以下几行代码 是等价的:

C语言 Page 1

static const int i; const static int i; int const static i; const int static i; 根据C语言规范,在进行句法分析的时候,typedef和存储类型指示符是等价的!所以,我们把上述使用 static的地方替换为typedef: typedef const int i; const typedef int i; int const typedef i; const int typedef i; 上述代码的语义是:将i定义为一个类型名,其等价的类型为const int。以后如果我们有i a代码,就 等价于const int a。对于有指针的地方也是一样的,比如: int const typedef *t;那么代码t p。就相当于int const *p。 另外,typedef不能和static等存储类型指示符同时使用,因为每个变量只能有一种存储类型,所以代 码:typedef static int i;是非法的。 typedef有两种用法: 一、一般形式,定义已有类型的别名 typedef 类型 定义名; 二、创建一个新的类型
typedef 返回值类型 新类型名(参数列表); 1)typedef int NUM[10];//声明整型数组类型
NUM n;//定义n为整型数组变量,其中n[0]--n[9]可用 2)typedef char* STRING;//声明STRING为字符指针类型
STRING p,s[10];//p为字符指针变量,s为指针数组 3)typedef int (*POINTER)();//声明POINTER为指向函数的指针类型,该函数返回整型值,没有参数
POINTER P1,P2;//p1,p2为POINTER类型的指针变量
说明: 1)用typedef可以声明各种类型名,但不能用来定义变量,用typedef可以声明数组类型、字符串
类型、使用比较方便。 例如:定义数组,原来是用:int a[10],b[10],c[10],d[10];由于都是一维数组,大小也相同,可以先将 此数组类型声明为一个名字: typedef int ARR[10]; 然后用ARR去定义数组变量: ARR a,b,c,d;//ARR为数组类型,它包含10个元素。因此a,b,c,d都被定义为一维数组,含10个元素。可以 看到,用typedef可以将 数组类型 和 数组变量 分离开来,利用数组类型可以定义多个数组变量。同样
C语言 Page 2

看到,用typedef可以将 数组类型 和 数组变量 分离开来,利用数组类型可以定义多个数组变量。同样 可以定义字符串类型、指针类型等。
2)用typedef只是对已经存在的类型增加一个类型名,而没有创造新的类型。 3)typedef与#define有相似之处,但事实上二者是不同的,#define是在 预编译 时处理,它只能 做简单的字符串替换,而typedef是在 编译时 处理的。它并不是做简单的字符串替换,而是采用如同 定 义变量 的方法那样来 声明 一个类型。 例如:typedef int COUNT;和#define COUNT int的作用都是用COUNT代表int,单事实上它们二者是不同 的。 两个陷阱: 陷阱一: 记住,typedef是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换。比如: 先定义: typedef char* PSTR; 然后: int mystrcmp(const PSTR, const PSTR); const PSTR实际上相当于const char*吗?不是的,它实际上相当于char* const。 原因在于const给予了整个指针本身以常量性,也就是形成了常量指针char* const。 简单来说,记住当const和typedef一起出现时,typedef不会是简单的字符串替换就行。 陷阱二: typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然 它并不真正影响对象的存储特性,如: typedef static int INT2; //不可行 编译将失败,会提示“指定了一个以上的存储类”。 1.typedef 函数指针的使用方法 (1)typedef 首先是用来定义新的类型,i.e typedef struct {.....}mystruct; 在以后引用时,就可以用 mystruct 来定义自己的结构体,mystruct structname1,mystruct structname2. (2)typedef 常用的地方,就在定义函数指针,行为和宏定义类似,用实际类型替换同义字,但是有区 别: typedef 在编译时被解释,因此让编译器来应付超越预处理器能力的文本替换。 案例一: 通常讲,typedef要比#define要好,特别是在有指针的场合。请看例子: typedef char *pStr1; #define pStr2 char *; pStr1 s1, s2; pStr2 s3, s4; 在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是我们所预期的指针变 量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字。
C语言 Page 3

量,根本原因就在于#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++错误。
用法一: typedef 返回类型(*新类型)(参数表) typedef int ( * MYFUNCTION )( int,int ); 这种用法一般是在定义函数指针 MYFUNCTION 是一个函数
指针类型 有两个整型的参数,返回一个整型。 在对于这样的形式,去掉typedef和别名 就剩下了的是原变量的类型 如:int (*)(int ,int); 在函数指 针中,抽象得看待函数,函数名其实就是一个地址,函数名指向该函数的代码在内存的首地址。 用法二: 复杂函数声明类型
下面是三个变量的声明 用typedef 如何来做??? >1 int *(*a[5])(void *,void *); >2 void (*b[5])(void (*)()); >3 float (*)()(*pa)[10]; 分析如下: >1 int *(*a[5])(void *,void *); //pFUN是自己建立的类型别名 typedef int *(* pFUN)(void *,void *); //等价于int *(*a[5])(void *,void *); pFUN a[5]; a是一个数组,包含五个元素,这些元素都是函数指针,该函数指针所指的函数的返回值是 int的指针 输入参数有两个都是void *. >2 void (*b[5])( void (*)() ); // first 为蓝色的 声明一个新的类型 typedef void (*pFUNParam)( ); //整体声明一个新类型 typedef void (*pFUN)(FUNParam); //使用定义的新类型声明对象 等价于void (*b[5])( void (*)() ); pFUN b[5]; b 是一个含有5个元素的数组,每个元素都是一个函数指针,该函数指针所指的函数的返回值 是void.输入参数是另一个函数指针,这个函数指针没有参数,返回值为空。在这里套用了连续的函数指
C语言 Page 4

是void.输入参数是另一个函数指针,这个函数指针没有参数,返回值为空。在这里套用了连续的函数指 针。本身就是一个函数指针,而且参数也是一个函数指针。 >3 float (*)()(*pa)[10]; //first 为上面的蓝色表达式声明一个新类型 typedef float (*pFUN)(); //整体声明一个新类型typedef pFUN (* pFunParam)[10]; //使用定义的新类型来声明对象 等价与float (*)()(*pa)[10]; pa 是一个指针,指针指向一个含有10个元素的数组,数组的元素是函数指针,函数指针所指的函数没有输 入参数,返回值为float. ********************************************** 使用typedef简化复杂的变量声明 1)、定义一个有10个指针的数组,该指针指向一个函数,该函数有一个整形参数,并返回一个整型? 第一种方法:int (*a[10])(int); 第二种方法:typedef int (*pfunc)(int);
pfunc a[10]; 2)、定义一个有10个指针的数组,该指针指向一个函数,该函数有一个函数指针(不带参数,返回值为 空)参数,并返回空。 第一种方法:void (*a[10])(void (*)(void)); 第二种方法:typedef void (*pfuncParam)(void);
typedef void (*pfunc)(pfuncParam); pfunc a[10]; 3)、一个指向有10个函数指针(不带参数,返回值为double)数组的指针 第一种方法:double (*)(void) (*p)[10]; 第二种方法:typedef double (*pfunc)(void);
typedef pfunc (*pfuncParam)[10]; pfuncParam p; 从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;括号内分析完就跳出括号,还是按 先右后左的顺序,如此循环,直到整个声明分析完。举例: 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*类型的形参,
C语言 Page 5

组的元素是函数类型的指针,它指向的函数具有int*类型的形参,
返回值类型为int。
也可以记住2个模式:
type (*)(....)函数指针
type (*)[]数组指针 ********************************************** finally
typedef 使用最多的地方是创建易于记忆的类型名,用它来归档程序员的意图。类型出现在所声明的变量 名字中,位于 ''typedef'' 关键字右边。例如:
typedef int size;此声明定义了一个 int 的同义字,名字为 size。注意 typedef 并不创建新的类型。 它仅仅为现有类型添加一个同义字。你可以在任何需要 int 的上下文中使用 size: void measure(size * psz); size array[4];size len = file.getlength();std::vector <size> vs; 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()函数的头文件 <csinal>,一个有同样接口的函数。
typedef 和存储类关键字(storage class specifier)
C语言 Page 6

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<char, char_traits<char>,allocator<char>> 和 basic_ofstream<char, char_traits<char>>。
C语言 Page 7




友情链接: