Happy New Year 

伴随着2018的寒冬,2019就这么来临了,互联网裁员也是此起彼伏。公司为裁员各出奇招斗智斗勇。

前有某赞的非人性化工作制度以及惊人论点,现有某蜂以需要逻辑能力为借口进行数学考试。某锤也已倒下,未来怎样,犹未可知。

但显然,一味的担心明显是没有任何用处,不如冷静沉着应对,提升自己,正所谓:金子总会发光的。

 

c语言中,typedef就是用来给某一个类型定义一个别名。

相当于给一个人起一个外号而已,但外号本就是个可有可无的东西。

从这个角度来看,typedef明明没有存在的必要,可却应用广泛,究竟是为何?

万物存在即有理,既然它可以存在那么久并且得到广泛的应用,那一定是有它的道理。

为了心甘情愿接受typedef的存在,也为了以后更好的使用typedef,决定来分析一下typedef的用法,来看看它究竟有何魔力

typedef的用法大致分为两种

  • 为现有的数据类型创建一个别名

  • 为复杂的数据类型创建一个别名

为现有数据类型创建一个别名

比如type作用域unsigned int 

1typedef unsigned int U_INT;

规定U_INT是一种unsigned int无符号整型的别称

1U_INT b;//等价于unsigned int b;声明一个unsigned int型的b.

 再来看一下type作用于struct

1typedef struct item {
2        char a;
3        int b;
4 } Item;

规定Item是struct item的别称

从目前来看typedef貌似也没起什么不可或缺的作用,只是稍微的省略了一两个单词而已,也不足以说服我接受typedef的存在

但是,如果要封装接口的话用处可就大了,

比如针对上文的Item,如果再进行一个struct封装

1typedef struct node{
2         Item * clause;
3         Item * next;
4 } Node;

接口开发人员提供针对Node的操作方法

 1/**file1.h*/ 
2typedef struct item {
3        char a;
4        int b;
5} Item;
6typedef struct node{
7         Item * clause;
8         Item * next;
9} Node
10void InitialNode(Node * node);
11bool AddNode(Node* node, Item item);
12CopyToNode(Item item, Node * node);

实现接口方法

 1/**file1.c*/ 
2void InitialNode(Node * node){
3      node = NULL;
4 }
5 bool AddNode(Node* node, Item item){
6        Node* last = node;       
7        Node* pnew;
8        pnew = (Node*)malloc(sizeof(Node));
9           if(pnew == NULL) {
10             return false;
11        }
12        CopyToNode(item, pnew);
13        pnew->next = NULL;
14        if(last == NULL){
15             last = pnew;
16        }else{
17             while(last->next != NULL){
18                 last = last->next;
19             }
20             last->next = pnew;
21        }
22        return true;
23
24 }
25
26 CopyToNode(Item item, Node * node){
27         node->next = item;
28 }

对于接口开发人员来说,InitalNode和AddNode方法的输入相关的是Node指针和Item指针,函数中的操作也是针对Node和Item的整体而言.

那么如果你想替换item,接口函数也无需再重新书写,只需要重新定义一个struct,用typedef重命名为Item即可

1typedef struct custom{
2     int a;
3     int arr[10];
4     char str[100];
5} Item;

item也可以是基本类型

1typedef int Item;

而node无需重新定义,用户依旧可以调用那一套统一接口

如果学过面向对象语言的话,很明显看出来这就是多态啊。用户只需要重新声明一个别名为Item的类型(无论是什么,struct也好,基本类型也好),就可以直接调用统一的接口实现

由此,typedef的意义才真正彰显出来。虽然c语言中没有多态的概念,但是却用typedef实现了多态。使得接口代码有了可移植性。

为复杂的数据类型创建一个别名

比如为数组创建别名

1typedef int INT_ARRAY_100[100];

INT_ARRAY_100是一个数组的别称,该数组含有100个int型元素

1INT_ARRAY_100 arr; //等价于int arr[100]; 

比如为函数指针创建别名

1typedef char * PF(char *,int *);

PF代表的是返回值为char * ,参数为char*, int*的函数

那么在使用时

1void func(PF pf);//等价于void func(char * pf(char * ,int *)

看起来用typedef确实舒服多了。typedef针对于复杂数据类型,可以让代码看起来更简洁。

那么,其实综上可以看出来,typedef存在不外乎两个原因

  • 为复杂的类型创建一个容易识别的简洁的别名

  • 为接口的可移植提供遍历

注意:

利用typedef为指针命名时应该注意一点:如下代码中p代表的是char类型的指针

1typedef char * p;

也就是说p本身是一个指针,指针类型为char;

那么问题就来了

1const p 

这句代码代表什么意思呢?表示声明一个常量,该常量是char类型的指针。p代表的是指针是一个常量,也就是p所指向的地址不可以改变,但是所指向的值可以修改

那么和以下哪句代码等价呢?

1//声明一个指针,指向常量,可修改指针指向别处,但指针指向的内容不可修改
2const char * p;
3//声明一个常量,但该常量属于指针类型,也就是常亮指针,一旦初始化就不可指向别处地址
4char * const p; 

根据注释,不难看出,相当于第二个声明。也就是说可以有如下操作

1 int a = 1;
2 int b = 2;
3 const p = &a;
4 p = &b;//不正确,不能修改p的指向地址
5 *p = b;//正确,可以修改指向地址中存储的值

这就是和#define不同之处了,#define只是单纯的字符串代替

1#define POINT int *
2int one = 1;
3int two = 2;
4const POINT point = &one;
5point = &two;//正确,可以修改p的指向地址
6*point = two;//不正确,不可以修改指向地址中存储的值

 

https://mp.weixin.qq.com/s/3lR50B3U4aonFG9C-sFQmA