typedef用法和陷阱
一、typedef的用法
1.用typedef来声明新的类型名,来代替已有的类型名,也就是给类型起别名。比如
1 typedef float real; //用real来代表float类型 2 real a; //定义一个real类型的变量,等价于float a,即定义一个float类型的变量a
这种用法经常用来作为定义与平台无关的类型,方便代码的跨平台移植。
例如,定义real类型为目标平台精度最高的类型。
1>在支持long double的平台上定义为:
typedef long double real;
注:long double为c99增加的类型,ansi c标准规定了double变量存储为 ieee 64 位(8 个字节)浮点数值,但并未规定long double的确切精度,但规定long double的精度不少于double的精度。所以对于不同平台long double可能有不同的实现,有的是8字节,有的是10字节,有的是12字节或16字节。关于具体的编译器的情况, 可以通过 sizeof(long double)得知。
2>在不支持long double的平台上改为
typedef double real;
3>在double不支持的平台上改为
typedef float real;
因此,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。
2.为自定义数据结构(如struct、union)定义简洁易记的类型名。
比如:
struct data{ int month; int day; int year; }; //定义一个data的结构体
声明结构体变量时,代码如下
struct data birthday;
用typedef可以定义新的类型,代替上面的结构体类型
typedef struct data{ int month; int day; int year; }data; //定义一个data的结构体 data birthday; //定义一个data类型的变量birthday,等价于上面struct data类型
注:这种方法在一些代码规范中是不推荐和禁止的,因为新的类型隐藏了具体的类型,不利于看代码的人直观了解到实际类型。
3.用typedef来声明新的类型
1>可以用来简化数组变量定义
typedef int arr[10]; //定义类型arr为含有10个元素的int数组 arr arr_a, arr_b; //定义了两个含有10个元素的int数组变量arr_a,arr_b
2>简化指针变量的定义
typedef int* int_p int_p p1,p2; //定义两个int指针变量p1、p2
注意,下面的定义容易引起错误,所以编程规范中禁止一次声明多个变量
int* p1, p2; //p1为int*类型,p2为int类型
1 typedef char* string; 2 strint p; //p为字符串指针变量 3 strint s[10]; //s为指针数组,即s为一个含有10个元素的数组,每个元素是一个字符串指针 4 5 typedef int (* pointer)(); //声明pointter为指向函数的指针类型,该函数返回值为int类型 6 pointer p1,p2; //p1,p2均为函数指针变量;
4.为复杂的声明定义一个新的简单的别名。
int *(*a[5])(int, char*); //变量名为a,直接用一个新别名pfun替换a typedef int *(*pfun)(int, char*); pfun a[5]; //定义一个含有5个函数指针的数组
void (*b[10]) (void (*)()); //原声明,b为含有10个元素的函数指针数组,其参数为不带参数的函数指针 typedef void (*pfunparam)();//声明不带参数的函数指针为类型pfunparam typedef void (*pfunx)(pfunparam); //声明pfunx为函数指针 pfunx b[10]; //函数指针数组b
1 doube(*)() (*e)[9]; //变量e为指针,该指针指向一个数组,该数组含有9个元素,每个元素为一个函数指针,该函数指针的返回值为double类型 2 简化写法: 3 typedef double(*pfuny)(); //声明一个函数指针别名pfuny,返回类型为double 4 typedef pfuny (*pfunparamy)[9]; //声明一个指针 5 pfunparamy e;//声明变量e为pfunparamy类型
理解复杂声明可用的“右左法则”:
从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。举例:
int (*func)(int *p);
首先找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明(*func)是一个函数,所以func是一个指向这类函数的指针,即函数指针,这类函数具有int*类型的形参,返回值类型是int。
int (*func[5])(int *);
func右边是一个[]运算符,说明func是具有5个元素的数组;func的左边有一个*,说明func的元素是指针(注意这里的*不是修饰func,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。
也可以记住2个模式:
type (*)(....)函数指针
type (*)[]数组指针
二、typedef的陷阱
1.typedef是一种新类型的声明,不是简单的字符串替换,不同于#define
例如:
1 typedef char* string; 2 int strcmp(const string,const string);
在上面的代码中,“const string” 是否相当于 “const char*” 呢?
答案是否定的,原因很简单,typedef 是用来声明一种新的类型,它不同于宏,不是简单的字符串替换。string已经是一种类型,const修改该类型为常量。因此,“const string”中的 const 给予了string常量属性,也就是整个指针本身常量性,也即形成了常量指针“char*const(一个指向char的常量指针)”。
即它实际上相当于“char*const”,而不是“const char*(指向常量 char 的指针)”。当然,要想让 const pchar 相当于 const char* 也很容易,如下面的代码所示:
typedef const char* pchar; int strcmp(pchar, pchar);
因此,无论什么时候,只要为指针声明 typedef,那么就应该在最终的 typedef 名称中加一个 const,以使得该指针本身是常量。
2.typedef 和存储类关键字(storage class specifier)
typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然它并不真正影响对象的存储特性。
例如下面的代码会编译报错:
1 typedef static int int_static;
参考:
https://www.cnblogs.com/a-s-m/p/10995722.html
http://c.biancheng.net/view/298.html
推荐阅读
-
SQL事务用法begin tran,commit tran和rollback tran的用法
-
html5各种页面切换效果和模态对话框用法总结
-
PHP 函数call_user_func和call_user_func_array用法详解
-
matlab中分号、冒号、逗号等常用标点符号的功能和用法总结
-
ES6 Promise对象的含义和基本用法分析
-
JQuery的加载和选择器用法简单示例
-
call和apply的区别是什么(apply的用法和搭配)
-
Android搜索框(SearchView)的功能和用法详解
-
Android搜索框SearchView属性和用法详解
-
Android搜索框(SearchView)的功能和用法详解