typedef讲解
typedef关键字的作用是可以用于给数据类型定义一个别名
举个例子,比如说你的名字叫 列夫·尼古拉耶维奇·托尔斯泰 ,我嫌弃这个名字太长了,所以给你取一个别名,叫 托尔斯泰 ,以后我叫 托尔斯泰 的时候你就知道在叫你了。
下面对 typedef 几种用法进行讲解:
1、typedef创建结构体别名
当你定义了一个结构体时,每次创建一个结构体都要使用struct+结构体名
的方式,而用了typedef之后,只要一个结构体别名
就可以创建了。
struct app_configure { //本名
unsigned char name;
unsigned char size;
unsigned char color;
};
struct app_configure app1; //必须使用struct
struct app_configure app2;
typedef创建结构体别名
typedef struct app_configure {
unsigned char name;
unsigned char size;
unsigned char color;
} App_Config; //别名
App_Config app1; //不需要用struct
App_config app2;
此外,用typedef 创建结构体别名时,可以省略本名
typedef struct { //省略本名
unsigned char name;
unsigned char size;
unsigned char color;
} App_Config; //别名
App_Config app1; //不需要用struct
App_config app2;
2、typedef创建数据类型的别名
我们知道C语言定义数据类型的时候只定义了它们之间的关系,但却没有具体定义它们的大小。比如 short 的长度只规定了不大于 int,long的长度不小于 int,int是多大也没确定,所以你会看到51单片机的int大小为两个字节,而在stm32中的长度为 4 字节。所以这个时候有必要使用一个别名来代替具体的数据类型,并且最好这个别名有一定的说明性:
typedef signed char int8_t;
typedef signed short int int16_t;
typedef signed int int32_t;
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
这样做的好处时代码移植后,我就知道怎么根据新平台修改这个定义了,因为这个别名已经有长度、符号信息了。
从这个角度来看typedef
和使用#define
是有相同的效果。那么为什么还用使用typedef
,确实两者功能类似, 但是#define 严格来说它只是用来替换的,而typedef 是专业的。所以使用 #define 可能一不小心就会给你挖坑了。
#define pCHAR char*
pCHAR pStr1,pStr2; //pStr1 是指针,而pStr2不是
上面这个例子中,由于#define
只是进行简单的替换,所以实际上的形式是这个样子的。
char * pStr1, pStr2; //pStr1是指针, 而pStr2是char 变量
而如果使用typedef
就没有这个问题。
typedef char* pCHAR; //pCHAR是别名
pCHAR pStr1, pStr2; //pStr1, pStr2都是指针
3、注意项:
虽说typedef在取别名上是专业的,但是如果你不熟悉而贸然使用的话也可能会为你挖坑的。下面介绍使用typedef时的一些坑:
1、Const
在和const一起使用的时候,本想定义一个指向的字符为常量的变量指针,但因为typedef的特殊性,不是简单的替换,所以最终的定义的是指向的字符为变量的常量指针。
typedef char* pCHAR; //定义char指针的别名
int func(const pCHAR, const pCHAR); //想定义: 指针为变量,指向的字符为常量
//实际: 指针为常量,指向的字符为变量
解决的办法就是在typedef中加const即可:
typedef const char* pCHAR; //定义char指针的别名
int func( pCHAR, pCHAR); //指针为变量,指向的字符为常量
拓展:
const char * const pString1 = "char const, point const"; //指针为常量,指向的字符为常量
char const * const pString1 = "char const, point const"; //指针为常量,指向的字符为常量
char* const pString1 = "char volatile, point const"; //指针为变量,指向的字符为常量
const char* pString1 = "char const, point volatile"; //指针为变量,指向的字符为常量
char* pString1 = "char volatile, point volatile";//指针为变量,指向的字符为变量
2、其余出错提示
typedef 不影响对象的存储特性,但是在语法上它却是一个存储类的关键字,就像 auto、extern、static、register等关键字一样。所以不能和存储类的关键字一起使用:
以下都是错的
typedef extern char* pCHAR;
typedef static char* pCHAR;
typedef auto char* pCHAR;
typedef register char* pCHAR;
4、 typedef 定义函数指针
typedef void (*pfun)(void); // pfun 类型是 void(*)(void)
pfun main; // 定义一个函数:void (*main)(void);
下面可以介绍一个经典的函数指针的应用。
#include<stdio.h>
typedef int (*FP_CALC)(int, int);
//注意这里不是函数声明而是函数定义,它是一个地址,你可以直接输出add看看
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return b? a/b : -1;
}
//定义一个函数,参数为op,返回一个指针。该指针类型为 拥有两个int参数、
//返回类型为int 的函数指针。它的作用是根据操作符返回相应函数的地址
FP_CALC calc_func(char op)
{
switch (op)
{
case '+' : return add; // 返回函数的地址
case '-' : return sub;
case '*' : return mul;
case '/' : return div;
default:
return NULL;
}
return NULL;
}
//s_calc_func为函数,它的参数是 op,返回值为一个拥有两个int参数、返回类型为int 的函数指针
int (*s_calc_func(char op)) (int, int)
{
return calc_func(op);
}
//最终用户直接调用的函数,该函数接收两个int整数,和一个算术运算符,返回两数的运算结果
int calc(int a, int b, char op)
{
FP_CALC fp = calc_func(op); // 根据预算符得到各种运算的函数的地址
int (*s_fp)(int, int) = s_calc_func(op); // 用于测试
// ASSERT(fp == s_fp); // 可以断言这俩是相等的
if (fp){
return fp(a, b); //根据上一步得到的函数的地址调用相应函数,并返回结果
}
else{
return -1;
}
}
void main()
{
int a = 100, b = 20;
printf("calc(%d, %d, %c) = %d\n", a, b, '+', calc(a, b, '+'));
printf("calc(%d, %d, %c) = %d\n", a, b, '-', calc(a, b, '-'));
printf("calc(%d, %d, %c) = %d\n", a, b, '*', calc(a, b, '*'));
printf("calc(%d, %d, %c) = %d\n", a, b, '/', calc(a, b, '/'));
}