typedef用法
1、
typedef
此声明定义了一个
size
例如:
Line
相当于char
typedef的语法规则其实很简单,一句话来说就是定义对象的语法前加关键字typedef,剩下的不变,原本定义的对象标识符换成类型标识符,对应语义从定义一个对象改成定义一个类型别名。typedef看起来复杂根本原因是对象定义的语法比较复杂,例如分隔符*和[]的用法。 typedef struct node{ datatype data; struct node *lchild,*rchild; }bintnode; 对应的对象定义: struct node{ datatype data; struct node *lchild,*rchild; }bintnode; 去除bintnode来看就是一个类型struct node的完整描述。加上了bintnode来看,表示定义了一个struct node类型的对象bintnode。 现在前面有typedef,因此这个bintnode不是对象名而是类型名。也就是定义了一个类型别名bitnode,实际上指的就是struct node这个完整类型。 typedef定义的类型别名在作用域内和被定义的原类型语义上等价,都是表示同一个类型的名称。这里typedef之后bitnode可以和struct node互相代替(注意在C++中,如果同一命名空间内类型名和对象名没有重复,那么struct可以省略,struct node等价于node)。 更复杂一点的: struct node{ datatype data; struct node *lchild,*rchild; }bintnode, *bintree; 注意定义对象时*修饰对象本身而不是修饰类型。因此这里定义了struct node对象bintnode和struct node*类型的对象bintree。 对应的类型定义: struct node{ datatype data; struct node *lchild,*rchild; }bintnode, *bintree; 这里定义了类型别名bintnode表示完整类型struct node,以及bintree表示类型struct node*。 拆开来就成了 typedef struct node{ datatype data; struct node *lchild,*rchild; }bintnode; typedef bintnode *bintree; 这种写法看起来应该稍微清楚了一点。 至于下面的cirqueue,和上面的bintnode类似,只是这里省略了结构体名称,是一个匿名类型。这样写的主要好处是类型名可以省略struct(C++里面不这样写也可以省略)。
Typedef和define都可以用来给对象定义另外一个别名,但是两者却有着很大不同。
1.
关键字typedef在编译阶段有效,由于是在编译阶段,因此typedef有类型检查的功能。
Define则是宏定义,发生在预处理阶段,也就是编译之前,它只进行简单而机械的字符串替换,而不进行任何检查。
#define用法例子:
#define f(x) x*x
main( )
{
int a=6,b=2,c;
c=f(a) / f(b);
printf("%d
}
程序的输出结果是: 36,根本原因就在于#define只是简单的字符串替换。
2.
Typedef用来定义类型的别名,这些类型不只包含内部类型(int,char等),还包括自定义类型(如struct),可以起到使类型易于记忆的功能。
如: typedef int (*PF) (const char *, const char *);
定义一个指向函数的指针的数据类型PF,其中函数返回值为int,参数为const char *。
typedef 有另外一个重要的用途,那就是定义机器无关的类型,例如,你可以定义一个叫 REAL 的浮点类型,在目标机器上它可以i获得最高的精度:typedef long double REAL;
在不支持 long double 的机器上,该 typedef 看起来会是下面这样:typedef double REAL;
并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样:typedef float REAL;
#include
typedef int arr[10];
void main()
{
}
#define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。
3.
#define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用。
而typedef有自己的作用域。
void
4.
二者修饰指针类型时,作用不同。
Typedef int * pint;
#define PINT int *
Const pint p;//p不可更改,p指向的内容可以更改,相当于 int * const p;
Const PINT p;//p可以更改,p指向的内容不能更改,相当于 const int *p;或 int const *p;
pint s1, s2; //s1和s2都是int型指针
PINT s3, s4; //相当于int * s3,s4;只有一个是指针。
typedef的四个用途和两个陷阱
用途一:
定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象。比如:
char*
//
以下则可行:
typedef
PCHAR
虽然:
char
也可行,但相对来说没有用typedef的形式直观,尤其在需要大量指针的地方,typedef的方式更省事。
用途二:
用在旧的C代码中(具体多旧没有查),帮助struct。以前的代码中,声明struct新对象时,必须要带上struct,即形式为:
struct
{
};
struct
而在C++中,则可以直接写:结构名
tagPOINT1
估计某人觉得经常多写一个struct太麻烦了,于是就发明了:
typedef
{
}POINT;
POINT
或许,在C++中,typedef的这种用途二不是很大,但是理解了它,对掌握以前的旧代码还是有帮助的,毕竟我们在项目中有可能会遇到较早些年代遗留下来的代码。
用途三:
用typedef来定义与平台无关的类型。
比如定义一个叫
typedef
在不支持
typedef
在连
typedef
也就是说,当跨平台时,只要改下
标准库就广泛使用了这个技巧,比如size_t。
另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健(虽然用宏有时也可以完成以上的用途)。
用途四:
为复杂的声明定义一个新的简单的别名。方法是:在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。举例:
1.
变量名为a,直接用一个新别名pFun替换a就可以了:
typedef
原声明的最简化版:
pFun
2.
变量名为b,先替换右边部分括号里的,pFunParam为别名一:
typedef
再替换左边的变量b,pFunx为别名二:
typedef
原声明的最简化版:
pFunx
3.
变量名为e,先替换左边部分,pFuny为别名一:
typedef
再替换右边的变量e,pFunParamy为别名二
typedef
原声明的最简化版:
pFunParamy
理解复杂声明可用的“右左法则”:从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。举例:
int
首先找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明 (*func)是一个函数,所以func是一个指向这类函数的指针,即函数指针,这类函数具有int*类型的形参,返回值类型是int。
int
func右边是一个[]运算符,说明func是具有5个元素的数组;func的左边有一个*,说明func的元素是指针(注意这里的*不是修饰func,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。
也可以记住2个模式:
type
type
---------------------------------
陷阱一:
记住,typedef是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换。比如:
先定义:
typedef
然后:
int
const
原因在于const给予了整个指针本身以常量性,也就是形成了常量指针char*
简单来说,记住当const和typedef一起出现时,typedef不会是简单的字符串替换就行。
陷阱二:
typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然它并不真正影响对象的存储特性,如:
typedef
编译将失败,会提示“指定了一个以上的存储类”。
#define,const,typedef三者简单的联系与区别
一. #define与const
联系:都可以用来定义常量
区别:
1. const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应)。
2. 前者在堆栈分配了空间,而后者只是把具体数值直接传递到目标变量罢了。或者说,const的常量是一个Run-Time的概念,他在程序中确确实实的存在并可以被调用、传递。而#define常量则是一个Compile-Time概念,它的生命周期止于编译期:在实际程序中他只是一个常数、一个命令中的参数,没有实际的存在。
3.const常量存在于程序的数据段,#define常量存在于程序的代码段。
4. 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试、
常量是常数或代表固定不变值的名字。
程序中如果想让变量的内容自初始化后一直保持不变,可以定义一个常量。
例如,在圆面积计算中经常要用常数丌,此时,通过命名一个容易理解和记忆的名字来改进程序的可读性,同时在定义中加关键字const,给它规定为常量性质,以帮助预防程序出错。
如果在整个程序中的许多地方都要用到一个常数,那么,在这些地方的一个或多个地方写错了这个值就会导致计算错误。如果给常数取个名字,每处常数都以该名字代替,那么编译器就能检查名字拼写错误,避免常数值的不一致性。
丌字符不属于C++语言的字符描述集,不能用来作C++中的名字。我们用pi来表示丌:
const float pi=3.1415926;
由于有效位的限制,在下面常量定义中,最后3位不起作用:
const float pi=3.141592653;
尽管等号后面的常数是double型的,但因为float常量只能存储7位有效位精度的实数,所以pi的实际值为3.141593(最后1位4舍5人)。如果将常量pi的类型改为double型,则能全部接受上述10位数字。
定义成const后的常量,程序中对其只能读不能修改,从而可以防止该值被无意地修改。由于不可修改,所以,常量定义时必须初始化。例如:
const float pi;
pi=3.1415926; //error
常量名不能放在赋值语句的左边。
常量定义中初始化的值可以是一个常量表达式。常量在程序运行之前就已经知道了其值,所以,编译时就能求值。但表达式中不能含有某个函数。例如:
const int size=100 * sizeof(int); //ok
const int number=max(15,23); //error
因为sizeof不是函数,而是C++的基本操作符,该表达式的值在编译之前能确定,所以第一个常量定义语句合法。第二个语句要求函数值,函数一般都要在程序开始运行时才能求值,该表达式不能在编译之前确定其值,所以是错误的。
一般来说,相同类型的变量和常量在内存中占有相同大小的空间。只不过常量不能通过常量名去修改其所处的内存空间,而变量却可以。
在C中,另一种定义常量的方法是用编译预定义指令(#define)。例如:
#define PI 3.1415926
这条指令的格式是#define后面跟一个常量名再跟一串字符, 中间用空格隔开。由于它不是C++语句,所以行末不用分号。
当程序被编译时,它要先被编译预处理。当预处理遇到#define指令时,就用数值3.1415926替换程序中所有的PI。
尽管它具有常量的所有属性,但是,在编译预处理完成后,PI不属于C++程序中的名字了(全部被字串3.1415926所替代),所以它不是一个具有一定类型的常量名。随后的编译无法发现由它引起的数据类型误用的错误。
C++容许#define定义常量是为了兼容C,使C程序能在C++编译器中顺利通过。在C++编程中,常量定义都用const,不用#define。