时间:2023-03-09来源:系统城装机大师作者:佚名
总结一句话:“加不加typedef,类型是一样的",这句话可以这样理解:
没加typedef之前如果是个数组,那么加typedef之后就是数组类型;
没加typedef之前如果是个函数指针,那么加typedef之后就是函数指针类型;
没加typedef之前如果是个指针数组,那么加typedef之后就是指针数组类型。
1 2 3 4 |
typedef char TA[5]; //定义数组类型 typedef char *TB[5]; //定义指针数组类型,TB定义的变量为含5个char*指针元素的数组(指针数组类型) typedef char *(TC[5]); //指针数组类型,因为[]的结合优先级最高,所以加不加()没啥区别,TC等价于TB typedef char (*TD)[5]; //数组指针类型,TD指向一个5容量的char数组 |
后文简单介绍下指针数组和数组指针
1、给已定义的变量类型起个别名
(1)typedef unsigned char uin8_t; //uint8_t就是unsigned char的别名,这是最基础的用法;
(2)结构体用法——作用是给struct __person起了个别名person_t,这种这种用法也很基础;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
struct __person { char name[20]; uint8_t age; uint8_t height; } typedef __person person_t; //以上两段代码也可合并为一段,如下: typedef struct __person { char name[20]; uint8_t age; uint8_t height; }person_t; |
2、定义函数指针类型
首先来看一下如何定义函数指针变量,然后再看如何定义函数指针类型。
(1)定义函数指针变量
定义了一个函数指针变量pFunc,它可以指向这样的函数:返回值为int,形参为char*、int:
1 | int (*pFunc)( char *frame, int len); |
定义了5个函数指针变量:pFunc[0]、pFunc[1]···,它们都可以指向这样的函数:返回值为int*,形参为int:
1 | int *(*pFunc[5])( int len); |
(2)定义函数指针类型
定义函数指针类型,必须使用typedef,方法就是,在“定义函数指针变量”前加上typedef。
1 | typedef int (*pFunc_t)( char *frame, int len); //定义了一个类型pFunc_t |
举例:
1 2 3 4 5 6 7 8 9 10 11 12 |
typedef int (*pFunc_t)( char *frame, int len); //定义了一个类型pFunc_t int read_voltage( char *data, int len) { int voltage = 0; ··· //其他功能代码 return voltage; } int main( void ) { pFunc_t pHandler = read_voltage; //使用类型pFunc_t来定义函数指针变量 ··· //其他功能代码 } |
3、定义数组指针类型
这个问题还是分两步,先看如何定义数组指针变量,再看如何定义数组指针类型。
(1)定义数组指针变量
int(*pArr)[5];//定义了一个数组指针变量pArr,pArr可以指向一个int [5]的一维数组
char(*pArr)[4][5];///定义了一个数组指针变量pArr,pArr可以指向一个char[4][5]的二维数组
char(*pArr)[4][5];///定义了一个数组指针变量pArr,pArr可以指向一个char[4][5]的二维数组
1 2 3 4 5 6 7 8 |
int (*pArr)[5]; //pArr是一个指向含5个int元素的一维数组的指针变量 int a[5] = {1,2,3,4,5}; int b[6] = {1,2,3,4,5,6}; pArr = &a; //完全合法,无警告 pArr = a; //发生编译警告,赋值时类型不匹配:a的类型为int(*),而pArr的类型为int(*)[5] pArr = &a[0]; //发生编译警告,赋值时类型不匹配:a的类型为int(*),而pArr的类型为int(*)[5] pArr = &b; //发生编译警告,赋值时类型不匹配:&b的类型为int(*)[6],而pArr的类型为int(*)[5] pArr = ( int (*)[5])&b; //类型强制转换为int(*)[5],完全合法,无警告 |
上面这个例子中,使用类型转换时,代码的样式略显复杂,试想,我们如果强转为一个结构体数组的指针,那这个强转的括号里的内容得多长!这就直接影响了代码的可读性,因此,强转后的类型应该定义出来。
(2)定义数组指针类型
如同上面定义函数指针类型的方法,直接在前面加typedef即可,例如:
1 | typedef int (*pArr_t)[5]; //定义了一个指针类型pArr_t,该类型的指针可以指向含5个int元素的数组 |
1 2 3 4 5 6 7 8 9 10 |
typedef int (*pArr_t)[5]; //定义一个指针类型,该类型的指针可以指向含5个int元素的一维数组 int main( void ) { int a[5] = {1,2,3,4,5}; int b[6] = {1,2,3,4,5,6}; pArr_t pA; //定义数组指针变量pA pA= &a; //完全合法,无警告 pA= (pArr_t)&b; //类型强制转换为pArr_t,完全合法,无警告 } |
(3)定义数组类型
如果我们想声明一个含5个int元素的一维数组,一般会这么写:int a[5];
如果我们想声明多个含5个int元素的一维数组,一般会这么写:int a1[5], a2[5], a3[5]···,或者 a[N][5]
可见,对于定义多个一维数组,写起来略显复杂,这时,我们就应该把数组定义为一个类型,例如:
1 | typedef int arr_t[5]; //定义了一个数组类型arr_t,该类型的变量是个数组。 |
1 2 3 4 5 6 7 8 9 10 11 |
typedef int arr_t[5]; int main( void ) { arr_t d; //d是个数组,这一行等价于: int d[5]; arr_t b1, b2, b3; //b1, b2, b3都是数组 d[0] = 1; d[1] = 2; d[4] = 134; d[5] = 253; //编译警告:下标越界 } |
4、为复杂的声明定义一个新的简单的别名
为复杂的声明定义一个新的简单的别名。方法是:在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。举例:
1 2 3 4 |
int *(*a[5])( int , char *); //原声明 typedef int *(*pFun)( int , char *); //变量名为a,直接用一个新别名pFun替换a就可以了 pFun a[5]; //原声明的最简化版 |
1 2 3 4 5 6 |
void (*b[10]) ( void (*)()); //原声明 typedef void (*pFunParam)(); //变量名为b,先替换右边部分括号里的,pFunParam为别名一 typedef void (*pFunx)(pFunParam); //再替换左边的变量b,pFunx为别名二 pFunx b[10]; //原声明的最简化版 |
陷阱一:记住,typedef是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换。
陷阱二:typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然它并不真正影响对象的存储特性。
案例一:
通常讲,typedef要比#define要好,特别是在有指针的场合。请看例子:
1 2 3 4 |
typedef char *pStr1; #define pStr2 char *; pStr1 s1, s2; pStr2 s3, s4; |
在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是我们所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字。
案例二:
下面的代码中编译器会报一个错误,你知道是哪个语句错了吗?
1 2 3 4 5 6 |
typedef char * pStr; char str[4] = "abc" ; const char *p1 = str; const pStr p2 = str; p1++; p2++; |
是p2++出错了。这个问题再一次提醒我们:typedef和#define不同,它不是简单的文本替换。上述代码中const pStr p2并不等于const char * p2。const pStr p2和const long x本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已。因此,const pStr p2的含义是:限定数据类型为pStr 的变量p2为只读,因此p2++错误。
对于指针数组和数组指针的概念,相信很多人经常会感到迷惑,见到二者一时不能分辨究竟对应哪一个才是对的。接下来我们来分析一下二者区别。
我们来看一下这个示例代码:
1 2 |
int *ptr1[10]; int (*ptr2)[10]; |
对于上述代码语句,你是否能分清哪一句代码声明的是指针数组,而哪一句代码声明的又是数组指针呢?
答案是第一行代码声明的ptr1是一个指针数组,数组名为 ptr1,而“int*”修饰的是数组的内容,该数组包含 5 个 指向 int 类型 数据的指针。第二行代码声明的ptr2则是一个 数组的指针 ,指针变量名为 ptr2,而 int 修饰的是数组的内容,即数组的每个元素。即ptr2 是一个指针,它指向一个包含 5 个 int 类型数据的数组。
这里其实可以首先看*能否与前面的类型结合,能的话,内容就是结合后的内容。
2024-07-07
Java框架如何实现非阻塞式编程?2023-03-11
Android Jetpack 组件LiveData源码解析2023-03-11
hbuilderx设置Firefox浏览器安装路径教程 hbuilderx怎么设置Firefox浏览器安装路径?一、AVL树的概念 二、AVL树节点的定义 三、AVL树的插入 四、AVL树的旋转 1.左单旋 2.右单旋 3.左右双旋 4.右左双旋 五、进行验证 六、AVLTree的性能...
2023-03-09