类型定义的语法可以归结为一句话:只要在变量定义前面加上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。在不指定存储类型指示符的时候,编译器会根据约定自动取缺省值。另外,存储类型指示符的位置也是任意的(但要求在变量名和指针*之前),也就是说以下几行代码是等价的:
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可以将 数组类型 和 数组变量 分离开来,利用数组类型可以定义多个数组变量。同样可以定义字符串类型、指针类型等。

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则是为一个类型起新名字。 
案例二: 
下面的代码中编译器会报一个错误,你知道是哪个语句错了吗? 
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.输入参数是另一个函数指针,这个函数指针没有参数,返回值为空。在这里套用了连续的函数指针。本身就是一个函数指针,而且参数也是一个函数指针。

>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*类型的形参,

返回值类型为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)
这种说法是不是有点令人惊讶,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 中typedef 函数指针的使用的更多相关文章

  1. C中typedef 函数指针的使用

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

  2. C++中使用函数指针 【瓦特芯笔记】

         在C++类中使用函数指针. 类型定义:      typedef 返回类型(类名::*新类型)(参数表) //类定义 class CA { public: char lcFun(int a) ...

  3. 1、C语言中的函数指针

    一 通常的函数调用 void MyFun(int x); //此处的申明也可写成:void MyFun( int ); int main(int argc, char* argv[]) { MyFun ...

  4. C语言结构体中的函数指针

      这篇文章简单的叙述一下函数指针在结构体中的应用,为后面的一系列文章打下基础 本文地址:http://www.cnblogs.com/archimedes/p/function-pointer-in ...

  5. C语言中的函数指针

    C语言中的函数指针 函数指针的概念:   函数指针是一个指向位于代码段的函数代码的指针. 函数指针的使用: #include<stdio.h> typedef struct (*fun_t ...

  6. (转)typedef 函数指针的用法

    typedef 函数指针的用法   在网上搜索函数指针,看到一个例子.开始没看懂,想放弃,可是转念一想,这个用法迟早要弄懂的,现在多花点时间看懂它,好过以后碰到了要再花一倍时间来弄懂它.其实很多时候都 ...

  7. typedef 函数指针的用法

    转自:http://www.cnblogs.com/shenlian/archive/2011/05/21/2053149.html typedef 函数指针的用法 在网上搜索函数指针,看到一个例子. ...

  8. C/C++中的函数指针

    C/C++中的函数指针 一.引子 今天无聊刷了leetcode上的一道题,如下: Median is the middle value in an ordered integer list. If t ...

  9. QT中使用函数指针

    想仿命令行,所以定义了一个类,让一个String 对应一个 function,将两者输入list容器. 类中定义了 QString commandStr; void (MainWindow::*com ...

随机推荐

  1. CSS padding margin border属性详解【转载】

    本文转载自:http://www.cnblogs.com/linjiqin/p/3556497.html ,感谢相关博主. 图解CSS padding.margin.border属性 W3C组织建议把 ...

  2. HTTP学习笔记5-常用报头

    24,在普通报头中,有少数报头域应用域所有的请求和响应消息,但并不用于被传输的实体,这些报头域只用于传输的消息. 25,常用的普通报头Cache-Control: Cache-Control普通报头域 ...

  3. c# WinForm开发 DataGridView各种操作总结大全

    一.单元格内容的操作 //取得当前单元格内容 Console.WriteLine(DataGridView1.CurrentCell.Value); // 取得当前单元格的列 Index Consol ...

  4. 关于Hibemate

    1.Hibernate定位 HIbernate是一款实现了ORM思想的框架 JDO TOpLink 2.HIbernate初次解释 Hibernate:冬眠,蛰伏 和持久化有关系 将内存中data持久 ...

  5. [week4]每周总结与工作计划

    计算机网络 TAT 小白dp 28号还有一场 背单词 背马克思 python目标80% 熟悉coursera c++模版和 仿函数 人文修养 开学数据库,itercast的sql*2 itercast ...

  6. CString与std::string unicode下相互转化

      1. CString to string CString str = L"test"; CString stra(str.GetBuffer(0)); str.ReleaseB ...

  7. How to configure Gzip for JBoss?---refer

    Question: I think to try to speed up my Web App by reducing the size of transferred data. For exampl ...

  8. Target runtime Apache Tomcat v6.0 is not defined. phyy Unknown Faceted Project Problem

    Description Resource Path Location TypeTarget runtime Apache Tomcat v6.0 is not defined. phyy Unknow ...

  9. ·数据库基本内容回顾-day16.06.30

    一. 模式的定义和删除  ---创建了一个模式,就创建了一个数据库命名空间,一个框架.cascade.restrict create schema<模式名> authorization & ...

  10. Creating Lists and Cards 创建列表和卡片

    To create complex lists and cards with material design styles in your apps, you can use the Recycler ...