由浅至深->C语言中typedef关键字的经典问题分析

引言:该系列的第三篇文章,哪怕仅一人受益也都达到了我分享的目的!

文章向导
令人迷惑的出场
typedef的基础玩法
typedef的进阶玩法(各种奇特的声明)
typedef .VS. #define

正文

一、令人迷惑的出场

    ~~~~       ~~~   C语言中总是存在着一些字面上使人困扰的关键字,typedef也不例外,明面上使用时给人一种“定义新类型”的感觉,或许你从未思索过上述问题,甚至还有些怀疑是否真的就是如此。实际上,typedef只能给已经存在的数据类型重命名,本质上不能产生新的类型,注意:它针对的是类型,而不是值,这点与#define不同。真相已经揭晓,C语言的创造者淡淡一笑,真的不是我的锅!

二、typedef的基础玩法

    ~~~~       ~~~   这里先介绍typedef关键字的一些简单用法,这也是作为后续进阶的铺垫。

typedef int Int32;

struct _tag_test
{
	int x;
	int y;
}typedef struct _tag_test Test; //位于已存在类型的后面

typedef struct
{
	int len;
	int array[];
} SoftArray; //struct别名式写法,详情参见第一篇文章

typedef struct _tag_list_node ListNode; //位于待定义类型的前面
struct _tag_list_node
{
	ListNode * next; //结构体指针变量
};

int main()
{
	Int32 i = -100; //int
	//unsigned Int32 j = 0; //error
	Test t;
	SoftArray * sa = NULL;
	ListNode * node = NULL;
	
	return 0;
}

上面这个简短的例子,可从中挖掘出两点有用的结论:

  • typedef重命名类型时,可位于已存在类型的后面以及待定义类型的前面。当然,上述展示的struct结构体在C语言中并不能算是一种类型,但暂且这样理解也没有什么太大的不妥。
  • 不能用unsigned或signed来修饰typedef重命名后的类型。

三、typedef的进阶玩法

1.奇特的声明

注意:前方高能,请在安全且舒适的环境下细细品读。

int box[8][8]; //int型二维数组
int ** ptr;  //指向int型的二维指针
int * ptr[10];  //指针数组,每个元素都是一个指向int的指针
int (*ptr)[10]; //数组指针,指向具有10个元素的int数组
int * ptr[3][4]; //同理为指针数组,略过...
int*ptr[3])[4]; //指针数组,一个具有3个元素的数组,每个元素都是一个指向具有4个元素的int数组的指针。
char* (*func[3])(char*); //指针数组(3个指针组成的数组),每个指针都指向返回为char*类型的函数,且该函数含有char*的参数。

有点晕? 哈哈,其实我并不是很想解释他们,别打我!先介绍下面这几点规则:

代表一个指针
()代表一个函数
[]代表一个数组
[]和()具有相同的优先级,都高于间接运算符*的优先级。
[]和()是从左到右结合的,那么在阅读时则应按照先后组合方式逐步剥开。

    ~~~~       ~~~   就以最后一个看起来最复杂的函数指针为例。首先,最中间的位置(*func[3]):[3]代表func为数组,但※号又表明它为指针,合并得到的信息就是指针数组;接着,既然是指针那必定就有指向,再往右看发现另一花括号(char※),则可得出为函数;最后,将中间的func部分给遮住就可以得出注释中的结论。
  的确还是有些绕,后面的指针部分的文章,我会力求做到更为简明的描述。

2.typedef化繁为简

    ~~~~       ~~~   上面的那些小点心可读性看起来还真是低呢,确实,事实上简单美一直是人们的一种追求,那么typedef给了我们一次打开美的机会。

(1)数组指针,交个朋友吧!

typedef type(name)[size]; /*命名数组类型的格式*/

typedef int(AINT5)[5];/*如这样,可直接使用AINT5作为类型定义变量*/
AINT5 a; //等价于int a[5];

AINT5* pi; //使用数组类型定义数组指针,等价于int (*pi)[5];
int (*pi)[5]; //直接定义数组指针

看完这个小例子,不知你是否发现这其中的简单和谐之美。

(2)函数指针,认识一下!

typedef type (name)(parameter list) /*命名函数类型的格式*/

typedef int (FUN)(int, int); /*如这样,可直接使用FUN标识定义变量*/
FUN* ptr; //使用函数类型定义指针,等价于int (*ptr)(int, int)

嗯,相信你已经明白它的妙用了,或许你也会与我一样爱上这种语言上的艺术和美感!


四、typedef .VS. #define

    ~~~~       ~~~   typedef与#define的差异在定义数据类型时则能体现出来,尤其是对指针操作时。

(1) 例1 (基本差异)

typedef char* PCR1;
#define PCR2  char*

PCR1 s1, s2;
PCR2 s3, s4;

    ~~~~       ~~~   此例中,变量s1,s2,s3都被定义为char*类型,只有s4被定义为char。而造成这种差异的根本原因:#define只是简单的文本替换,而typedef则是相当于新的类型名。

(2) 例2 (深层挖掘)

typedef char* p_str;
char str[] = "abc";
const char* p1 = str;
const p_str p2 = str;
p1++;
p2++;

    ~~~~       ~~~   上述代码存在一个较为隐蔽的错误(编译器会提示p2++错误)。该问题再次提示我们,typedef与#define的不同,即并非简单的文本替换。
  事实上,const p_str p2并不等于const char* p2(从内涵上来说),const p_str p2和const long x 没有本质区别,即都是赋予变量为只读属性。只不过上例中变量p2的数据类型是我们typedef出来的类型而不是系统固有的类型。
  因此,const p_str p2的含义为:限定数据类型为char的变量为只读(可将char想象成一种固有的类型如int来理解)。

参阅资料
C Primer Plus
高质量嵌入式Linux C编程
狄泰软件学院-C语言进阶剖析教程

下期预告:开始步入C语言中老大难的指针以及数组问题的分析!

你可能感兴趣的:(C/C++,语录)