C_2019_5_27(typedef的用法总结)

心随念走,身随缘走,人不能靠心情或者,而是靠心态或者!–大冰

1. 简化代码,提高程序的可读性

在程序中经常正确的使用 typedef 可以有效的增加代码的可读性、可维护性,试着比较下面的两段代码

//代码1
int (* myFunc(int (*pf)(const char *, const char*)))(const char *, const char*)

//代码2
typedef int (*PF)(const char*, const char*);
PF myfunc(PF pf);

相信 代码2 比 代码1 更容易理解也更方便维护吧。原因何在?

(1)typedef 行为有点像 define 宏,用其实际类型替代同义字;
(2)不同点是typedef 在编译时会被解释,因此让编译器来应付超越预处理器能力的文本替换。

  • 代码2的详细说明:

(1)这个声明引入了PF类型作为函数指针的别名,当中函数有两个const char*类型的参数以及一个int类型的返回值;
(2)myFunc() 的参数是一个 PF 类型的回调函数,返回某个函数的地址。

2. 定义易于记忆的类型名

(1)typedef 使用最多的地方是 创建易于记忆的类型名称,用它来表示我们的真实意图。也可以这么理解 在变量名字声明前面加上typedef,将变量提升为这个类型的别名(alias).

注意 :
(1)typedef并不创建新的类型。它仅仅为现有类型添加一个别名。例如:typedef int size; 定义了一个int的同义字size,你可以在任何需要int的上下文中使用size

(2)typedef 还可以掩饰复合类型,如指针和数组。例如下面的代码

//例如,你不用像下面这样重复定义有 20 个字符元素的数组:
char line[20];
char text[20];
//定义一个 typedef,每当要用到相同类型和大小的数组时,可以这样:
typedef char Line[81];
Line text, secondline;
getline(text);

隐藏指针

typedef char * pstr;
int mystrcmp(pstr, pstr);

标准函数 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); // 现在是OK的

强调一下:

只要为指针声明typedef,都要在最终的 typedef 名称前面加一个 const

3. typedef 与结构体的问题

    typedef struct Node
    {
      char *pData;
      pNode pNext;
    } *pNode;

这段代码可能你认为没问题,实际上编译器报了一个错误。上述代码的根本问题在于typedef的使用有误。
根据我们上面的阐述可以知道:新结构建立的过程中遇到了pNext域的声明,类型是pNode。但是此时在类型本身还没有建立完成的时候,这个类型的新名字也还不存在,也就是说这个时候编译器根本不认识pNode。

可以修改成下面的几种方式来解决此问题:

//方式1 -- 推荐使用
typedef struct Node
{
 char * pData;
 struct Node *pNext;
} *pNode;

//方式2
typedef struct Node *pNode;
struct Node
{
 char * pData;
 pNode pNext;
};

//方式3
struct Node
{
 char * pData;
 struct Node *pNext;
};
typedef struct Node *pNode;

4. typedef 和 define 的区别

typedef char *pStr;
define pStr char *;

上面两种定义pStr数据类型的方法,两者有什么不同?哪一种更好一点?

有人会说,”差不多都一样”.其实不是这样.

通常讲,typedef要比 define宏要好,特别是在有指针的场合。请看例子:

typedef char *PSTRING1;
#define PSTRING2 char *;
PSTRING1  p1, p2;
PSTRING2  p3, p4;

在上述的变量定义中,

p1、p2、sp3都被定义为char *,p4则定义成了char,而不是我们所预期的指针变量,根本原因在于 define 只是简单的字符串替换;而 typedef 则是为一个类型起新名字。

注意点:

(1)如果这样用 #define f(x) xx 是有问题的,应该这样 #define f(x) (xx) //即 一定要带()

(2)在许多C语言编程规范中提到使用 #define 定义时,如果定义中包含表达式,必须使用括号.

再如:

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++错误。
#define与typedef引申:

(1)#define优势:可以使用#ifdef ,#ifndef等来进行逻辑判断,使用#undef来取消定义。
(2)typedef优势:它受范围规则约束,使用typedef定义的变量类型其作用范围限制在所定义的函数或者文件内(取决于此变量定义的位置),而宏定义则没有这种约束。

5. typedef 为复杂的声明定义简单的别名时的理解规则:

理解复杂声明可用的 “右左法则”:

(1)从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;
(2)括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。

//示例1
int (*func)(int *p);

(1)首先,找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针;
(2)*然后,跳出这个圆括号,先看右边,又遇到圆括号,这说明(*func)*是一个函数.

结论:func是函数指针,且这类函数具有int*类型的形参,返回值类型是int。

//示例2
int (*func[5])(int *);

func右边是一个[]运算符,说明func是具有5个元素的数组;
func的左边有一个*,说明func的元素是指针
注意 :这里的不是修饰func,而是修饰func[5]的,原因是[]运算符优先级比高,func先跟[]结合跳出这个括号,看右边,又遇到圆括号.
结论:func数组的每一个元素是函数指针,它指向的函数具有int*类型的形参,返回值类型为int。
记住2个模式:

typedef (*)(....) //函数指针 
typedef (*)[]     //数组指针 

6. typedef 两大陷阱

陷阱一: typedef是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换.比如1:

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; 不可行,编译将失败,会提示“指定了一个以上的存储类”。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值