TypedefStruct⽤法详解
Typedef Struct ⽤法详解
⼀、typedef的⽤法
在C/C++语⾔中,typedef常⽤来定义⼀个标识符及关键字的别名,它是语⾔编译过程的⼀部分,但它并不实际分配内存空间。
实例像:
typedef int INT;
typedef int ARRAY[10];
typedef (int*) pINT;
typedef可以增强程序的可读性,以及标识符的灵活性,但它也有“⾮直观性”等缺点。
⼆、#define的⽤法
#define为⼀宏定义语句,通常⽤它来定义常量(包括⽆参量与带参量),以及⽤来实现那些“表⾯似和善、背后⼀长串”的宏,它本⾝并不在编译过程中进⾏,⽽是在这之前(预处理过程)就已经完成了,但也因此难以发现潜在的错误及其它代码维护问题,它的实例像:
#define INT int
#define TRUE 1
#define Add(a,b) ((a)+(b));
#define Loop_10 for (int i=0; i<10; i++)
⽤法详解:
1. #define 的变体,即 #ifndef,可以防⽌头头⽂件的重复引⽤。
#ifdef和 #define组合,⼀般⽤于头⽂件中,⽤以实现防⽌多个⽂件对此同⼀个头⽂件的重复引⽤.实际使⽤中,即使你的头⽂件暂时没有被多个⽂件所引⽤,为了增加程序可读性,移植性,健壮性等,还是最好都加上。其⽤法⼀般为:
#ifndef <;标识>
#define <;标识>
……… // include or define sth.
#endif
<;标识>在理论上来说可以是⾃由命名的,但每个头⽂件的这个“标识”都应该是唯⼀的 to void the definition duplication。but normallz, 标识的命名规则⼀般是头⽂件名全⼤写,前后加下划线,并把⽂件名中的“.”也变成下划线,如:stdio.h对应的就是:
#ifndef _STDIO_H_
#define _STDIO_H_
……… // include or define sth.
#endif
1. #define的变体,即#ifdef,可以实现加⼊⾃⼰需要的模块(源⽂件)
[例⼦] 在源⽂件中加⼊
#ifdef MYSELF_H
#include "myself.c"
#endif
可以实现在源⽂件中加⼊myself.c的代码,将其实现的功能加进来, 即加⼊了myself模块。
1. #define可以进⾏宏定义常量typeof的用法
可以对⼀些常见的变量,字符串等,进⾏宏定义,系统在编译期间,就会⾃动替换如果不进⾏宏定义,⼀般如果此类变量,字符串等,需要修改,就需要对源⽂件中它们出现的地⽅⼀⼀修改,效率⽐较低,⽽此种宏定义后,只需要修改⼀次,实现批量修改,效率较⾼.⽽且有些数字或字符很⿇烦,每次都要输⼊,就显得很繁琐,⽽且容易出错,⽽采取此宏定义,就很⽅便和易于维护.
[例⼦]
#define PI 3.1415926
[注意事项]
(1) 宏定义中的变量,约定俗成⽤⼤写,以此与⼩写的普通变量区分开来.当然如果你故意⼩写,也是合法的.不过如果你想让你写的程序具有⾼可读性,那最好遵
守此约定.
(2) #define的⾏尾,没有分号”;”,有些⼈不注意,会画蛇添⾜地加上.有些公司招聘时候的笔试,也会考察这个细节.
(3) 如果后⾯的宏定义中的变量和前⾯的有内在联系,那么后⾯的宏定义变量最好⽤前⾯的表⽰
[例⼦]
#define PI 3.1415926
#define RADIUS 5
⽽在表达该圆的⾯积的时候,就可以⽤下⾯的表⽰了:
#define AREA ((PI)*( RADIUS)*( RADIUS))
//此处加括号是为了避免后⾯提到的⼀种边界效应
[缺点]
宏定义有⼀些缺点:
(1) ⽆法对宏定义中的变量进⾏类型检查此缺点,是相对于const变量来说的
[define与const的区别的简单总结]
define定义的变量,是Compile-Time时期的变量,系统在编译时候,就将其全部替换,⽽不会对其变量进⾏类型等属性检查,相对不是很安全,可能存在潜在的问题,⽽没有发现. 正因为其仅仅是编译时期替换,所以其定义的变量,是不会在运⾏时候分配内存的,不占⽤内存空间. const定义的变量,是 Run-Time时期的变量,如果类型不匹配,系统在运⾏时候,就会发现并提⽰或报错,对应的,const变量在运⾏时期,也是⼀种变量,系统会为其分配内存.
(2) 边界效应
A. 未加括号带来的边界效应由于宏定义的时候,其各个分量未加括号,⽽在使⽤宏定义的时候,传递的参数是变量的表达式,然后经过系统展开后,由于优先级的原因,导致其结果不是你所希望的.
[例⼦]
#define MUL(A,B) A*B
⽽在使⽤的时候,这样的调⽤:
int a=1,b=2,c=3,d=0;
d=MUL(a+b,c)
经过编译时候展开,就变成了
d=a+b*c
⽽不是我们所希望的
d=(a+b)*c
[解决办法]
其解决办法也很简单,就是给每个分量,都加上括号,就可以避免此类问题即,在宏定义的时候,如此定义:
#define MUL(A,B) ((A)*(B))
B. 在define数据类型的时候, 未加括号带来的问题在⽤define进⾏新的数据类型定义的时候,由于未加括号,会出现你所未预料到的结果. 此点其实就是上⾯说的边界效应,之所以将此点单独说⼀下,是由于此点不是普通计算结果的问题,⽽是数据类型的问题,问题相对更严重. 也是笔者在看《想成为嵌⼊式程序员应知道的0x10个基本问题》的时候,看到其作者提到的这个问题,此处就⽤其例⼦解释如下:
[例⼦]
#define dPS struct s * //注意末尾⽆分号
当使⽤的时候,遇到:
dPS p1,p2;
的时候,经过编译时候替换扩展,就变成了
struct s* p1,p2;
⽽p2就不是我们所希望的s的指针类型了,⽽是⼀个普通的s数据结构类型的了.产⽣了边界效应.
[解决办法]
对应的解决办法也很简单,就是,遇到此类新数据类型定义的时候,还是⽤typedef 将上述宏定义改为:
typedef struct s * tPS; // 注意末尾有分号
⽽后的使⽤:
tPS p1,p2;
就正常了.
C. 特殊情况时候,加了括号也⽆法避免错误在宏定义中出现++或—之类的操作符的时候,即使加括号,也⽆法避免其中的问题
[例⼦]
#define MIN(A,B) ((A)<(B)?(A):(B))
如果进⾏如此调⽤
int a=1,b=3,min=0;
min=MIN(a++,b);
经过编译替换展开后,就成了
max=((a++)< (b)?(a++):(b))
计算出来的结果,就是
min=3
⽽不是我们所要的
min=1
此类问题⽆法避免,除⾮程序员在调⽤宏的时候,⾃⼰多加注意,尽量避免此类调⽤. about how to use the Macro of the mostly used function, like min() and max() ,please refer this: eliminate the side effect of Micro in C 【后记20100813】如果想要min的宏定义,避免传⼊a++,b++之类所导致的副作⽤,那么可以参考最新的Linux内核中的定义,如下:
#define min(x, y) ({
typeof(x) _x = (x);
typeof(y) _y = (y);
(void) (&_x == &_y);
_x < _y ? _x : _y; })
在Scott Meyer的Effective C++⼀书的条款1中有关于#define语句弊端的分析,以及好的替代⽅法,⼤家可参看。
三、typedef与#define的区别
从以上的概念便也能基本清楚,typedef只是为了增加可读性⽽为标识符另起的新名称(仅仅只是个别名),⽽#define原本在C中是为了定义常量,到了
C++,const、enum、inline的出现使它也渐渐成为了起别名的⼯具。有时很容易搞不清楚与typedef两者到底该⽤哪个好,如#define INT int这样的语句,⽤typedef⼀样可以完成,⽤哪个好呢?我主张⽤typedef 因为在早期的许多C编译器中这条语句是⾮法的,只是现今的编译器⼜做了扩充。为了尽可能地兼容,⼀般都遵循#define定义“可读”的常量以及⼀些宏语句的任务,⽽typedef则常⽤来定义关键字
、冗长的类型的别名。宏定义只是简单的字符串代换(原地扩展),⽽typedef则不是原地扩展,它的新名字具有⼀定的封装性,以致于新命名的标识符具有更易定义变量的功能。请看上⾯第⼀⼤点代码的第三⾏:
typedef (int*) pINT;
以及下⾯这⾏:
#define pINT2 int*
效果相同?实则不同!实践中见差别:
pINT a,b;的效果同int *a; int *b;表⽰定义了两个整型指针变量。
pINT2 a,b;的效果同int *a, b;表⽰定义了⼀个整型指针变量a和整型变量b。
注意:两者还有⼀个⾏尾;号的区别哦!
四、typedef struct与struct的区别
1.基本解释
typedef为C语⾔的关键字,作⽤是为⼀种数据类型定义⼀个新名字。这⾥的数据类型包括内部数据类型(int,char等)和⾃定义的数据类型(struct等)。
在编程中使⽤typedef⽬的⼀般有两个,⼀个是给变量⼀个易记且意义明确的新名字,另⼀个是简化⼀些⽐较复杂的类型声明。
⾄于typedef有什么微妙之处,请你接着看下⾯对⼏个问题的具体阐述。
当⽤下⾯的代码定义⼀个结构时,编译器报了⼀个错误,为什么呢?莫⾮C语⾔不允许在结构中包含指向它⾃⼰的指针吗?请你先猜想⼀下,然后看下⽂说明:
typedef struct tagNode
{
char *pItem;
pNode pNext;
} *pNode;
答案与分析:
1. typedef的最简单使⽤
typedef long byte_4;
//给已知数据类型long起个新名字,叫byte_4。
2. typedef与结构结合使⽤
typedef struct tagMyStruct
{
int iNum;
long lLength;
} MyStruct;
这语句实际上完成两个操作:
1) 定义⼀个新的结构类型
struct tagMyStruct
{
int iNum;
long lLength;
};
分析:tagMyStruct称为“tag”,即“标签”,实际上是⼀个临时名字,struct 关键字和tagMyStruct⼀起,构成了这个结构类型,不论是否有typedef,这个结构都存在。
我们可以⽤struct tagMyStruct varName来定义变量,但要注意,使⽤tagMyStruct varName来定义变量是不对的,因为struct 和tagMyStruct合在⼀起才能表⽰⼀个结构类型。
2) typedef为这个新的结构起了⼀个名字,叫MyStruct。
typedef struct tagMyStruct MyStruct;
因此,MyStruct实际上相当于struct tagMyStruct,我们可以使⽤MyStruct varName来定义变量。
C语⾔当然允许在结构中包含指向它⾃⼰的指针,我们可以在建⽴链表等数据结构的实现上看到⽆数这样的例⼦,上述代码的根本问题在于typedef的应⽤。
根据我们上⾯的阐述可以知道:新结构建⽴的过程中遇到了pNext域的声明,类型是pNode,要知道pNode表⽰的是类型的新名字,那么在类型本⾝还没有建⽴完成的时候,这个类型的新名字也还不存在,也就是说这个时候编译器根本不认识pNode。
解决这个问题的⽅法有多种:
1)、
typedef struct tagNode
{
char *pItem;
struct tagNode *pNext;
} *pNode;
2)、
typedef struct tagNode *pNode;
struct tagNode
{
char *pItem;
pNode pNext;
};
注意:在这个例⼦中,你⽤typedef给⼀个还未完全声明的类型起新名字。C语⾔编译器⽀持这种做法。
五、在C和C++中struct和typedef struct的区别
在C和C++有三种定义结构的⽅法。
typedef struct {
int data;
int text;
} S1;
//这种⽅法可以在c或者c++中定义⼀个S1结构
struct S2 {
int data;
int text;
};
// 这种定义⽅式只能在C++中使⽤,⽽如果⽤在C中,那么编译器会报错
struct {
int data;
int text;
} S3;
//这种⽅法并没有定义⼀个结构,⽽是定义了⼀个s3的结构变量,编译器会为s3内存。
void main()
{
S1 mine1;// OK ,S1 是⼀个类型
S2 mine2;// OK,S2 是⼀个类型
S3 mine3;// OK,S3 不是⼀个类型
S1.data = 5;// ERROR S1 是⼀个类型
S2.data = 5;// ERROR S2 是⼀个类型
S3.data = 5;// OK S3是⼀个变量
}
另外,对与在结构中定义结构本⾝的变量也有⼏种写法
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论