C&C++学习笔记(1)——typedef

C++中typedef的复杂用法很多,特别容易出现在面试题中,这一章用于总结typedef的用法。

本文仅供个人记录和复习,不用于其他用途

typedef的基本用法

typedefdefine宏替换不同,它不是简单的文本替换,而是类型替换。我们在开发的过程中,经常会出现类型特别复杂的情况。为了方便,我们通常用typedef进行简化,减少工作量。最简单的用法是用别名替换类型名,比如typedef int* pI。很多时候,我们不一定是为了简化类型名,而是为了给类型名起一个别名,方便我们理解。

扩展的用途

在C程序中,我们实例化struct结构体时,必须用struct + 结构体名 + 变量名的形式,而在C++中则不需要加上struct。那么为了简单一点,可以用typedef简化,这样一来,我们可以直接使用POINT来创建结构体变量:

1
2
3
4
5
6
7
typedef struct Position
{
int x;
int y;
}POINT;
POINT p1;

另外呢,在实际开发的过程中,可以使用typedef来定义与平台无关的类型。比如在win32平台,int为4个字节,而当处于Linux平台时,int只有2个字节。那么,为了方便起见,可以将int类型起一个别名:

1
2
3
typedef int INT;
// 更换为4个字节的类型
typedef long INT;

这样一来,我们在编程的过程中便直接使用INT。当平台中的int类型不再是4个字节时,我们可以使用4个字节的变量来代替它(比如Linux中的long类型),不再需要我们手动一个个地在代码中更改。

复杂声明的替换

事实上,typedef用得最多的还是这个方面。替换时记住一个规则,就是把变量名留到最后,先替换复杂的声明。下面呢,我将举出一些常见的例子:

1
2
3
4
5
// 这是一个函数指针的数组
int *(*a[5])(int, char*);
// 替换后
typedef int *(*pFun)(int, char*);
pFun a[5];

这里不是很复杂,直接替换变量名就可以了。

1
2
3
4
5
6
7
// 这是一个函数指针的数组,参数为函数指针
void (*b[10]) (void (*)());
// 先替换参数
typedef void (*pParam)();
// 再替换变量名
typedef void (*pFunc)(pParam);
pFunc b[10];

这个的参数比较复杂,先替换参数,再替换变量名。

1
2
3
4
5
6
7
// 这是一个指向数组的指针,数组由函数指针构成
double(*)() (*e)[9];
// 先替换左边
typedef double(*pFunc)();
// 再替换变量名
typedef pFunc (*pPoint)[9];
pPoint e;

由于指针指向的数组是包含了函数指针,因此先将函数指针替换,再替换变量名。

如何判断一个复杂声明

虽然我上面讲述了如何替换,但大家可能还是不知道,如何判断原来的表达式是什么类型。这里告诉大家一个简单的方法,那就是从变量名开始解读,先往右,碰到括号再往左,然后再跳出括号判断。下面举一些例子:

1
int *&a;

从右往左,首先是一个引用,引用的是一个指向int类型的指针。

1
int (*func)(int *p);

func是一个指针,跳出括号,发现是一个函数,那么func是一个函数指针。这个函数的返回值是int类型,参数是指向int类型的指针。

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

向右看,func是一个数组,再向左看,这个数组包含的是指针。随后跳出括号,发现是一个函数。那么,func便是函数指针数组。

1
int(* z(int x, int (*y)(int)))(int);

不要被这一长串声明吓住了,先看变量名z的右边,很显然这是一个函数,这个函数的参数分别是一个int类型变量和一个函数指针。既然z是函数,那么肯定有返回值。看看左边,明显是一个函数指针。总的来讲,z是一个函数,有两个参数分别为整型变量和函数指针,返回值为函数指针。

注意事项

既然typedef不是简单的文本替换,那么必然会存在一些问题。下面的这个例子很好地说明了这一点:

1
2
typedef char *CH;
int mystrcmp(const CH, const CH);

按照一般的理解所推断,我们会理所当然的认为const CH就是const char *,事实上这是不正确的。CH所代表的类型是指向char类型的指针,const所作用的是指针而不是所指向的对象,因此,这里const应该是对指针进行了限制。

1
typedef static int INT2;

另外,上面的声明也是错误的,因为typedef是和autoexternmutablestaticregister等一样的,都是影响对象的存储特性的类型。尽管typedef并不影响对象的存储,但它确实是这种类型。

typedef和define的区别

翻看了许多资料,我发现了typedefdefine的一些不同之处。例如:

1
2
3
4
typedef char *pStr1;
#define pStr2 char *;
pStr1 s1, s2;
pStr2 s3, s4;

上面的s1s2s3都被定义为char *,而s4则定义成了char。从根本上来说,typedef是为类型起别名,而define只是简单的文本替换。