您的位置:首页 > 编程语言

《C专家编程》:语言类型的声明(三)

2016-05-22 20:54 316 查看
C语言在声明各种类型的变量,函数等的时候包含一个十分重要的东西,那就是声明器(declarator)-它是所有声明的核心。简单的说,声明器就是标识符以及与它组合在一起的任何指针、函数括号、数组下表等。有些事合法但是有些是非法的:

例如fool()(),或者foo()[]这都是非法的。

但是const int* (*p(int **p))(char *str,float p);这样的都是合法的。

一、结构-struct

结构就是把一些数据项结合在一起的数据结构。其他编程语言 把它称为记录(record)。进行组合的通常方法就是把需要组合的东西放在花括号里面。

struct fruit{//各种变量}fruit1,fruit2; //fruit1和fruit2都是同一种类型;
在C语言中,struct关键字不能省略;

另外有两个跟结构有关的参数传递问题。有些C语言书籍称:“在调用函数时,参数是按照从右到左压入栈里。”这种说法太简单了!事实上,参数在传递的过程中,应尽可能的放入寄存器中(追求速度)。注意int型变量i跟只包含一个int型成员的结构变量s在参数传递的过程中可能完全不同。一个int类型的参数一般会将其放入寄存器中,而结构参数很可能被传入到堆栈中。

二、联合-union

联合在其他语言中被称为变体记录(variant record)。它的外表与结构体类似,但在内存的布局上有着关键性的区别。在结构中每个成员一次存储,而在联合中,所有的成员都从偏移地址零开始存储。这样,每个成员的位置重叠在一起:某一时刻,只有一个成员真正存储于该地址。

联合一般被用来节省空间,因为有些数据项是不可能同时出现的,如果同时存储他们显的有些浪费空间。就好比要记录一块显示屏上某一时刻的数据信息:出现的数字有可能是int,float,也有可能是字符串,有可能是图片等等;但是某一个时刻就只能显示其中一个,我们也只需要某一时刻的数据,这时候我们就可以用union数据结构,这样不仅能存储所有的数据类型,还节省了大量的空间。

例如下面的程序:

union
{
int num;
struct{char a,b,c,d}byte;
}value;
这样我们可以轻易地取得单独字节字段,如:value.byte.a。不需要额外的空间赋值或者强制类型转换。

三、枚举-enum

枚举通过一种简单的途径,把一串名字与一串整形数值联系在一起。但是在C语言中,很少只有枚举能完成而#define不能完成,所以大多数早期的K&RC编译器中,都省掉了枚举。不过由于枚举在大说书语言宏也存在,所以C语言最终也是实现了它。

enum number{small=5,medium,large=10,humungous};//类似于定义了一堆宏;
缺省情况下,整形值从零开始。赋值后,后面的以此+1;

注意:#define和枚举还是有一个很大的区别,这也是枚举的优点。#define定义的名字一般在编译器时被丢弃,而枚举名字则通常一直在调试器中可见,可以在调试代码的时候看见,这方变了软件设计者们find bug。

这也是在Effective C++中条款一:尽量使用inline和const代替#define的主要原因之一。

四、C语言声明的优先级原则:

理解C语言的优先级可以轻松帮我们搞定C语言声明:

例如在开篇提到的合法的申明:const int* (*p(int **p))(char *str,float p);

它到底是什么意思呢?这就需要我们熟悉C语言的优先级:

优先级法则:

声明从它的名字开始读起,然后按照优先级顺序一次读取。

优先级从高到底依次是:

(1)声明中被括号括起来的部分;

(2)后缀操作符:

括号()表示这是一个函数;

方括号[]表示这是一个数组;

(3)前缀操作符:*表示“指向...的指针”;

那么开篇的声明解释为:p是一个函数,函数有一个参数,该参数是一个指向指针的指针,该指针指向int类型;返回值是一个指针,是一个指向函数的指针,该函数有两个参数,一个是指向char类型的指针,一个是float类型的值;该函数返回值是一个指针,是一个指向只读的整型数据的指针。

五、typedef

typedef是一种有趣的声明,它为一种类型引入新的名字,而不为变量分配内存空间。typedef类似于宏文本替换,因为它并没有引入新的类型。而是为现有的类型取一个新的名字。

一般情况下,typedef用于简洁的表示指向其他东西的指针。

还是以开篇提到的声明为例:
const int* (*p(int **p))(char *str,float p);
这个声明看起来不那么友好,那么怎样使其看起来比较简洁呢?这个时候typedef就派上了用场。

typedef const int *(*pFun)(char *str,float p);

那么此时该声明可以简化为:

pFun p(int **p);

我们还可把上述的声明继续复杂化:

const int* (*p(int **p,void (*pFun1)(int a)))(char *str,float p);
看这个声明,其实也没什么,在复杂也不要怕,一层层的拨开它的真面目,其实真的没什么?

看我们定义两个类型:

typedef void (*pFunt1)(int );
typedef const int *(*pFunt2)(char *str,float p);
那么上式简化为:pFunt2 p(int **p,pFunt1 );
这样一看就十分明了,一个函数带两个参数,就是这么简单,没有什么大不了的地方!

前面说了typedef和#define类似,但是它们还是会有关键性的区别:

(1)#define定义的宏可以用其他类型进行扩展,但是typedef不行:

例如:
#define INT int
unsigned INT a;//OK,这里没有问题;
//但是:
typedef int INT;
unsigned INT a;//ERROR,这是非法的;
(2)typedef定义的类型能够保证声明中所有的变量均为同一类型,但是#define不行:

例如:
typedef int* INTeger;
INTeger a,b;//这里a,b都是int *类型的;
//但是:
#define INTeger int*
INTeger a,b;//这里a是int*,但是b是int类型的;
注:typedef应该使用在数组、结构、指针以及函数的组合类型。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: