C语言相关.docx
《C语言相关.docx》由会员分享,可在线阅读,更多相关《C语言相关.docx(15页珍藏版)》请在冰豆网上搜索。
C语言相关
#ifndef
目录[隐藏]
定义
补充一些内容
[编辑本段]
定义
#ifndefx
#definex
...
#endif
这是宏定义的一种,它可以根据是否已经定义了一个变量来进行分支选择,一般用于调试等等.实际上确切的说这应该是预处理功能中三种(宏定义,文件包含和条件编译)中的一种----条件编译。
C语言在对程序进行编译时,会先根据预处理命令进行“预处理”。
C语言编译系统包括预处理,编译和链接等部分。
#ifndefx
//先测试x是否被宏定义过
#definex
//如果没有宏定义下面就宏定义x并编译下面的语句
...
#endif
//如果已经定义了则编译#endif后面的语句
条件指示符#ifndef检查预编译常量在前面是否已经被宏定义。
如果在前面没有被宏定义,则条件指示符的值为真,于是从#ifndef到#endif之间的所有语句都被包含进来进行编译处理。
相反,如果#ifndef指示符的值为假,则它与#endif指示符之间的行将被忽略。
条件指示符#ifndef的最主要目的是防止头文件的重复包含和编译。
[编辑本段]
补充一些内容
头文件中的#ifndef(2007-09-3010:
07:
52)
标签:
学习公社
千万不要忽略了头件的中的#ifndef,这是一个很关键的东西。
比如你有两个C文件,这两个C文件都include了同一个头文件。
而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。
还是把头文件的内容都放在#ifndef和#endif中吧。
不管你的头文件会不会被多个文件引用,你都要加上这个。
一般格式是这样的:
#ifndef<标识>
#define<标识>
......
......
#endif
<标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。
标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:
stdio.h
#ifndef_STDIO_H_
#define_STDIO_H_
......
#endif
typedef
目录[隐藏]
typedef用法小结
代码简化
促进跨平台开发
C语言中typedef用法
[编辑本段]
typedef用法小结
在C语言的情况下,与C++稍有出入。
这两天在看程序的时候,发现很多地方都用到typedef,在结构体定义,还有一些数组等地方都大量的用到.但是有些地方还不是很清楚,今天下午,就想好好研究一下.上网搜了一下,有不少资料.归纳一下:
来源一:
UsingtypedeftoCurbMiscreantCode
Typedef声明有助于创建平台无关类型,甚至能隐藏复杂和难以理解的语法。
不管怎样,使用typedef能为代码带来意想不到的好处,通过本文你可以学习用typedef避免缺欠,从而使代码更健壮。
typedef声明,简称typedef,为现有类型创建一个新的名字。
比如人们常常使用typedef来编写更美观和可读的代码。
所谓美观,意指typedef能隐藏笨拙的语法构造以及平台相关的数据类型,从而增强可移植性和以及未来的可维护性。
本文下面将竭尽全力来揭示typedef强大功能以及如何避免一些常见的陷阱。
如何创建平台无关的数据类型,隐藏笨拙且难以理解的语法?
使用typedef为现有类型创建同义字。
定义易于记忆的类型名
typedef使用最多的地方是创建易于记忆的类型名,用它来归档程序员的意图。
类型出现在所声明的变量名字中,位于''typedef''关键字右边。
例如:
typedefintsize;
此声明定义了一个int的同义字,名字为size。
注意typedef并不创建新的类型。
它仅仅为现有类型添加一个同义字。
你可以在任何需要int的上下文中使用size:
voidmeasure(size*psz);
sizearray[4];
sizelen=file.getlength();
std:
:
vectorvs;
typedef还可以掩饰符合类型,如指针和数组。
例如,你不用象下面这样重复定义有81个字符元素的数组:
charline[81];
chartext[81];
定义一个typedef,每当要用到相同类型和大小的数组时,可以这样:
typedefcharLine[81];
Linetext,secondline;
getline(text);
同样,可以象下面这样隐藏指针语法:
typedefchar*pstr;
intmystrcmp(pstr,pstr);
这里将带我们到达第一个typedef陷阱。
标准函数strcmp()有两个‘constchar*'类型的参数。
因此,它可能会误导人们象下面这样声明mystrcmp():
intmystrcmp(constpstr,constpstr);
这是错误的,按照顺序,‘constpstr'被解释为‘char*const'(一个指向char的常量指针),而不是‘constchar*'(指向常量char的指针)。
这个问题很容易解决:
typedefconstchar*cpstr;
intmystrcmp(cpstr,cpstr);//现在是正确的
记住:
不管什么时候,只要为指针声明typedef,那么都要在最终的typedef名称中加一个const,以使得该指针本身是常量,而不是对象。
[编辑本段]
代码简化
上面讨论的typedef行为有点像#define宏,用其实际类型替代同义字。
不同点是typedef在编译时被解释,因此让编译器来应付超越预处理器能力的文本替换。
例如:
typedefint(*PF)(constchar*,constchar*);
这个声明引入了PF类型作为函数指针的同义字,该函数有两个constchar*类型的参数以及一个int类型的返回值。
如果要使用下列形式的函数声明,那么上述这个typedef是不可或缺的:
PFRegister(PFpf);
Register()的参数是一个PF类型的回调函数,返回某个函数的地址,其署名与先前注册的名字相同。
做一次深呼吸。
下面我展示一下如果不用typedef,我们是如何实现这个声明的:
int(*Register(int(*pf)(constchar*,constchar*)))
(constchar*,constchar*);
很少有程序员理解它是什么意思,更不用说这种费解的代码所带来的出错风险了。
显然,这里使用typedef不是一种特权,而是一种必需。
持怀疑态度的人可能会问:
"OK,有人还会写这样的代码吗?
",快速浏览一下揭示signal()函数的头文件,一个有同样接口的函数。
typedef和存储类关键字(storageclassspecifier)
这种说法是不是有点令人惊讶,typedef就像auto,extern,mutable,static,和register一样,是一个存储类关键字。
这并不是说typedef会真正影响对象的存储特性;它只是说在语句构成上,typedef声明看起来象static,extern等类型的变量声明。
下面将带到第二个陷阱:
typedefregisterintFAST_COUNTER;//错误
编译通不过。
问题出在你不能在声明中有多个存储类关键字。
因为符号typedef已经占据了存储类关键字的位置,在typedef声明中不能用register(或任何其它存储类关键字)。
[编辑本段]
促进跨平台开发
typedef有另外一个重要的用途,那就是定义机器无关的类型,例如,你可以定义一个叫REAL的浮点类型,在目标机器上它可以i获得最高的精度:
typedeflongdoubleREAL;
在不支持longdouble的机器上,该typedef看起来会是下面这样:
typedefdoubleREAL;
并且,在连double都不支持的机器上,该typedef看起来会是这样:
、
typedeffloatREAL;
你不用对源代码做任何修改,便可以在每一种平台上编译这个使用REAL类型的应用程序。
唯一要改的是typedef本身。
在大多数情况下,甚至这个微小的变动完全都可以通过奇妙的条件编译来自动实现。
不是吗?
标准库广泛地使用typedef来创建这样的平台无关类型:
size_t,ptrdiff和fpos_t就是其中的例子。
此外,象std:
:
string和std:
:
ofstream这样的typedef还隐藏了长长的,难以理解的模板特化语法,例如:
basic_string,allocator>和basic_ofstream>。
作者简介
DannyKalev是一名通过认证的系统分析师,专攻C++和形式语言理论的软件工程师。
1997年到2000年期间,他是C++标准委员会成员。
最近他以优异成绩完成了他在普通语言学研究方面的硕士论文。
业余时间他喜欢听古典音乐,阅读维多利亚时期的文学作品,研究Hittite、Basque和IrishGaelic这样的自然语言。
其它兴趣包括考古和地理。
Danny时常到一些C++论坛并定期为不同的C++网站和杂志撰写文章。
他还在教育机构讲授程序设计语言和应用语言课程。
[编辑本段]
C语言中typedef用法
1.基本解释
typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。
这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等)。
在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。
至于typedef有什么微妙之处,请你接着看下面对几个问题的具体阐述。
2.typedef&结构的问题
当用下面的代码定义一个结构时,编译器报了一个错误,为什么呢?
莫非C语言不允许在结构中包含指向它自己的指针吗?
请你先猜想一下,然后看下文说明:
typedefstructtagNode
{
char*pItem;
pNodepNext;
}*pNode;
答案与分析:
1、typedef的最简单使用
typedeflongbyte_4;
给已知数据类型long起个新名字,叫byte_4。
2、typedef与结构结合使用
typedefstructtagMyStruct
{
intiNum;
longlLength;
}MyStruct;
这语句实际上完成两个操作:
1)定义一个新的结构类型
structtagMyStruct
{
intiNum;
longlLength;
};
分析:
tagMyStruct称为“tag”,即“标签”,实际上是一个临时名字,struct关键字和tagMyStruct一起,构成了这个结构类型,不论是否有typedef,这个结构都存在。
我们可以用structtagMyStructvarName来定义变量,但要注意,使用tagMyStructvarName来定义变量是不对的,因为struct和tagMyStruct合在一起才能表示一个结构类型。
2)typedef为这个新的结构起了一个名字,叫MyStruct。
typedefstructtagMyStructMyStruct;
因此,MyStruct实际上相当于structtagMyStruct,我们可以使用MyStructvarName来定义变量。
答案与分析
C语言当然允许在结构中包含指向它自己的指针,我们可以在建立链表等数据结构的实现上看到无数这样的例子,上述代码的根本问题在于typedef的应用。
根据我们上面的阐述可以知道:
新结构建立的过程中遇到了pNext域的声明,类型是pNode,要知道pNode表示的是类型的新名字,那么在类型本身还没有建立完成的时候,这个类型的新名字也还不存在,也就是说这个时候编译器根本不认识pNode。
解决这个问题的方法有多种:
1)、
typedefstructtagNode
{
char*pItem;
structtagNode*pNext;
}*pNode;
2)、
typedefstructtagNode*pNode;
structtagNode
{
char*pItem;
pNodepNext;
};
注意:
在这个例子中,你用typedef给一个还未完全声明的类型起新名字。
C语言编译器支持这种做法。
3)、规范做法:
structtagNode
{
char*pItem;
structtagNode*pNext;
};
typedefstructtagNode*pNode;
3.typedefdefine的问题
有下面两种定义pStr数据类型的方法,两者有什么不同?
哪一种更好一点?
typedefchar*pStr;
#definepStrchar*;
答案与分析:
通常讲,typedef要比#define要好,特别是在有指针的场合。
请看例子:
typedefchar*pStr1;
#definepStr2char*
pStr1s1,s2;
pStr2s3,s4;
在上述的变量定义中,s1、s2、s3都被定义为char*,而s4则定义成了char,不是我们所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字。
上例中define语句必须写成pStr2s3,*s4;这这样才能正常执行。
#define用法例子:
#definef(x)x*x
main()
{
inta=6,b=2,c;
c=f(a)/f(b);
printf("%d\\n",c);
}
以下程序的输出结果是:
36。
因为如此原因,在许多C语言编程规范中提到使用#define定义时,如果定义中包含表达式,必须使用括号,则上述定义应该如下定义才对:
#definef(x)(x*x)
当然,如果你使用typedef就没有这样的问题。
4.typedefdefine的另一例
下面的代码中编译器会报一个错误,你知道是哪个语句错了吗?
typedefchar*pStr;
charstring[4]="abc";
constchar*p1=string;
constpStrp2=string;
p1++;
p2++;
答案与分析:
是p2++出错了。
这个问题再一次提醒我们:
typedef和#define不同,它不是简单的文本替换。
上述代码中constpStrp2并不等于constchar*p2。
constpStrp2和constlongx本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已。
因此,constpStrp2的含义是:
限定数据类型为char*的变量p2为只读,因此p2++错误。
#define与typedef引申谈
1)#define宏定义有一个特别的长处:
可以使用#ifdef,#ifndef等来进行逻辑判断,还可以使用#undef来取消定义。
2)typedef也有一个特别的长处:
它符合范围规则,使用typedef定义的变量类型其作用范围限制在所定义的函数或者文件内(取决于此变量定义的位置),而宏定义则没有这种特性。
5.typedef&复杂的变量声明
在编程实践中,尤其是看别人代码的时候,常常会遇到比较复杂的变量声明,使用typedef作简化自有其价值,比如:
下面是三个变量的声明,我想使用typdef分别给它们定义一个别名,请问该如何做?
>1:
int*(*a[5])(int,char*);
>2:
void(*b[10])(void(*)());
>3.double(*)()(*pa)[9];
答案与分析:
对复杂变量建立一个类型别名的方法很简单,你只要在传统的变量声明表达式里用类型名替代变量名,然后把关键字typedef加在该语句的开头就行了。
>1:
int*(*a[5])(int,char*);
//pFun是我们建的一个类型别名
typedefint*(*pFun)(int,char*);
//使用定义的新类型来声明对象,等价于int*(*a[5])(int,char*);
pFuna[5];
>2:
void(*b[10])(void(*)());
//首先为上面表达式蓝色部分声明一个新类型
typedefvoid(*pFunParam)();
//整体声明一个新类型
typedefvoid(*pFun)(pFunParam);
//使用定义的新类型来声明对象,等价于void(*b[10])(void(*)());
pFunb[10];
>3.double(*(*pa)[9])();
//首先为上面表达式蓝色部分声明一个新类型
typedefdouble(*pFun)();
//整体声明一个新类型
typedefpFun(*pFunParam)[9];
//使用定义的新类型来声明对象,等价于double(*(*pa)[9])();
pFunParampa;
struct
目录[隐藏]
结构类型定义和结构变量说明
一、结构的定义
二、结构的说明
1.1、默认的对齐方式
2.2、n字节的对齐方式
结构类型定义和结构变量说明
一、结构的定义
二、结构的说明
1.1、默认的对齐方式
2.2、n字节的对齐方式
[编辑本段]
结构类型定义和结构变量说明
在实际问题中,一组数据往往具有不同的数据类型。
例如,在学生登记表中,姓名应为字符型;学号可为整型或字符型;年龄应为整型;性别应为字符型;成绩可为整型或实型。
显然不能用一个数组来存放这一组数据。
因为数组中各元素的类型和长度都必须一致,以便于编译系统处理。
为了解决这个问题,C语言中给出了另一种构造数据类型——“结构”。
它相当于其它高级语言中的记录。
“结构”是一种构造类型,它是由若干“成员”组成的。
每一个成员可以是一个基本数据类型或者又是一个构造类型。
结构既是一种“构造”而成的数据类型,那么在说明和使用之前必须先定义它,也就是构造它。
如同在说明和调用函数之前要先定义函数一样。
[编辑本段]
一、结构的定义
定义一个结构的一般形式为:
struct结构名
{
成员表列
};
成员表由若干个成员组成,每个成员都是该结构的一个组成部分。
对每个成员也必须作类型说明,其形式为:
类型说明符成员名;
成员名的命名应符合标识符的书写规定。
例如:
structstu
{
intnum;
charname[20];
charsex;
floatscore;
};
在这个结构定义中,结构名为stu,该结构由4个成员组成。
第一个成员为num,整型变量;第二个成员为name,字符数组;第三个成员为sex,字符变量;第四个成员为score,实型变量。
应注意在括号后的分号是不可少的。
[编辑本段]
二、结构的说明
结构定义之后,即可进行变量说明。
凡说明为结构stu的变量都由上述4个成员组成。
由此可见,结构是一种复杂的数据类型,是数目固定,类型不同的若干有序变量的集合。
请看下面的结构:
structMyStruct
{
doubledda1;
chardda;
inttype
};
对结构MyStruct采用sizeof会出现什么结果呢?
sizeof(MyStruct)为多少呢?
也许你会这样求:
sizeof(MyStruct)=sizeof(double)+sizeof(char)+sizeof(int)=13
但是当在VC中测试上面结构的大小时,你会发现sizeof(MyStruct)为16。
你知道为什么在VC中会得出这样一个结果吗?
其实,这是VC对变量存储的一个特殊处理。
为了提高CPU的存储速度,VC对一些变量的起始地址做了“对齐”处理。
在默认情况下,VC规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。
下面列出常用类型的对齐方式(vc6.0,32位系统)。
类型
对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)
Char
偏移量必须为sizeof(char)即1的倍数
int
偏移量必须为sizeof(int)即4的倍数
float
偏移量必须为sizeof(float)即4的倍数
double
偏移量必须为sizeof(double)即8的倍数
Short
偏移量必须为sizeof(short)即2的倍数
1、默认的对齐方式
各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节VC会自动填充。
同时VC为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根