C Primer Plus之结构和其他数据形式
声明和初始化结构指针
声明结构化指针,例如:
struct guy * him;
初始化结构指针(如果barney是一个guy类型的结构),例如:
him = &barney;
注意:和数组不同,一个结构的名字不是该结构的地址,必须使用&运算符(类似普通变量)。
问题:如何使用指针来访问成员?
1° 使用一个新运算符:->
him->income is barney.income if him == &barney
2° 如果him = &barney,那么*him = barney,因为&和*是一对互逆的运算符。
barney.income == (*him).income
总之,如果him是指向名为barney的guy类型结构的指针,则下列表达式是等价的:
barney.income == (*him).income == him->income //假设him == &barney
C允许把一个结构赋值给一个结构,但不能对数组这样做,也就是说,如果n_data和o_data是同一类型的结构,那么:
o_data = n_data; // 把一个结构赋值给一个结构
这就使o_data的每个成员都被赋成n_data相应成员的值,即使有一个成员是数组也照样完成赋值。也可以把一个结构初始化为一个同样类型的结构:
struct names right_field = {"Ruthie", "George"};
struct names captain = right_field; // 把一个结构初始化为一个结构
结论:通常,程序员为了追求效率而使用结构指针作为参数;当需要保护数据、防止意外改变数据时对指针使用const限定词。传递结构值是处理小型结构最常用的方法。
C99具有一个称为伸缩型数组成员的新特性。利用这一新特性可以声明最后一个成员是一个具有特殊属性的数组的结构。该数组成员的特殊属性之一是它不存在,至少不立即存在。第二个特殊属性是您可以编写适当的代码使用这个伸缩型数组成员,就像它确实存在并且拥有您需要的任何数目的元素一样。
声明一个伸缩型数组成员的规则:
- 伸缩型数组成员必须是最后一个数组成员
- 结构中必须至少有一个其他成员
- 伸缩型数组就像普通数组一样被声明,除了它的方括号内是空的
例如:
struct flex
{
int count;
double average;
double scores[]; // 伸缩型数组成员
};
C99的意图并不是让您声明struct flex类型的变量;而是希望您声明一个指向struct flex类型的指针,然后使用malloc()来分配足够的空间,以存放struct flex结构的常规内容和伸缩型数组成员需要的任何额外空间。例如,假设想要用scores表示含有5个double型数值的数组,那么:
struct flex * pf;
pf = malloc(sizeof(struct flex) + * sizeof(double)); // 请求一个结构和一个数组的空间
例如:
#include <stdio.h>
#include <stdlib.h> struct flex {
int count;
double average;
double scores[]; // 伸缩型数组成员
};
void showFlex(const struct flex * p);
int main(void)
{
struct flex * pf1, * pf2;
int n = ;
int i;
int tot = ; pf1 = malloc(sizeof(struct flex) + n * sizeof(double)); // 没有指派类型,通用指针
pf1->count = n;
for(i = ; i < n; i++)
{
pf1->scores[i] = 20.0 - i;
tot += pf1->scores[i];
}
pf1->average = tot / n;
showFlex(pf1); n = ;
tot = ;
pf2 = malloc(sizeof(struct flex) + n * sizeof(double)); // 同上
pf2->count = n;
for(i = ; i < n; i++)
{
pf2->scores[i] = 20.0 - i / 2.0;
tot += pf2->scores[i];
}
pf2->average = tot / n;
showFlex(pf2);
free(pf2);
free(pf1); return ;
}
void showFlex(const struct flex * p)
{
int i;
printf("Scores: ");
for(i = ; i < p->count; i++)
printf("%g ", p->scores[i]);
printf("\nAverage: %g\n", p->average);
}
联合简介
联合(union)是一个能在同一个存储空间里(但不同时)存储不同类型数据的数据类型。一个典型的应用是一种表,设计它是用来以某种既没有规律、事先也未知的顺序保存混合类型数据。使用联合类型的数组,可以创建相同大小单元的数组,每个单元都能存储多种类型的数据。
联合是以与结构同样的方式建立的,也是需要有一个联合模板和一个联合变量。例如:
union hold {
int digit;
double bigfl;
char letter;
};
注意:具有类似声明的结构可以含有一个int型数值和一个double型数值以及一个char型数值,而这个联合可以含有一个int型数值或一个double型数值或一个char型数值。
下面是定义3个hold类型联合变量的例子:
union hold fit; // hold类型的联合变量
union hold save[]; // 10个联合变量的数组
union hold * pu; // 指向hold类型变量的指针
可以初始化一个联合。因为联合只存储一个值,所以初始化的规则与结构的初始化不同。具体地,有3种选择:
- 把一个联合初始化为同样类型的一个联合
- 初始化联合第一个元素
- 按照C99标准,可以使用一个指定的初始化项目
union hold valA;
valA.letter = 'R';
union hold valB = valA; // 把一个联合初始化为一个联合
union hold valC = {}; // 初始化联合的digit成员
union hold valD = {.bigfl = 118.2}; // 指定初始化项目
注意:对于联合这种数据类型,在同一个时间只能存储一个值。即使有足够的空间,也不能同时保存一个char类型和一个int类型的值。由您负责记住当前保存在联合中的数据的类型。
可以与指向联合的指针一起来使用->运算符
pu = &fit;
x = pu->digit; // 相当于 x = fit.digit
使用联合的适当场合:
- 使用一个成员来将值保存到一个联合中,该结构所存储的信息有赖于其中的一个成员。
- 在某些结构中,该结构所存储的信息有赖于其中的一个成员。
枚举类型
可以使用枚举类型声明代表整数常量的符号名称。通过使用关键字enum,可以创建一个新“类型”并指定它可以具有的值(实际上,enum常量是int类型的,因此在使用int类型 的任何地方都可以使用它)。枚举类型的目的是提高程序的可读性。
enum spectrum{red, orange, yellow, green, blue, violent};
enum spectrum color;
虽然枚举常量都是int类型的,但枚举常量较宽松地限定为任一种整数类型,只要改整数类型能保存这些枚举常量。
问:blue和red到底是什么?
答:从技术上讲,它们是int类型的常量。
red是一个代表整数0的命名常量,同样,其他标识符也是代表1到5的命名常量。在使用整数常量的任何地方都能使用枚举常量。
默认时,枚举列表中的常量被指定为整数值0、1、2等等。也可以选择常量具有的整数值,只须在声明中包含期望的值。如果只对一个常量赋值,而没有对后面的常量赋值,那么这些后面的常量会被赋予后续的值。
因为枚举类型是一个整数类型,所以enum变量能像整数变量那样被用在表达式中。
共享的名字空间
C使用术语名字空间(namespace)来表示识别一个名字的程序部分。作用域是这个概念的一部分;名字相同但具有不同作用域的两个变量不会冲突;而名字相同并在相同作用域中的两个变量就会冲突。名字、空间是分类别的。在一个特定作用域内的结构标记、联合标记以及枚举标记都共享同一个名字空间,并且这个名字空间与普通变量使用的名字空间是不同的,这意味着,可以在同一个作用域内对一个变量和一个标记使用同一个名字,而不会产错误;但是不能在同一作用域内使用名字相同的两个标记或名字相同的两个变量。例如:
struct rect{double x; double y};
int rect; // 在C中不会引起冲突
缺点:用两种不同的方式使用同一标识符会造成混乱;而且,C++不允许在同一个作用域内对一个变量和一个标记使用同一个名字,因为它把标记和变量名放在同一个名字空间中。
typedef简介
typedef工具是一种高级数据特性,它使您能够为某一类型创建自己的名字。在这个方面,它和#define相似,但是它们具有3个不同之处:
- 与#define不同,typedef给出的符号名称仅限于对类型,而不是对值
- typedef的解释由编译器,而不是预处理器
- 虽然它的范围有限,但在其受限范围内,typedef比#define更灵活
假设要对1字节的数值使用术语BYTE,您只须像定义一个char变量那样定义BYTE,然后在这个定义前面加上关键字typedef,如:
typedef unsigned char BYTE;
该定义的作用域取决于typedef语句所在的位置,如果定义是在一个函数内部,它的作用域就是局部的,限定在那个函数里。如果定义是在函数外部,它将具有全局作用域。
通常,这些定义使用大写字母,以提醒用户这个类型名称实际上是一个符号缩写,不过,您也可以使用小写字母。
typedef有助于增加可移植性。例如:
sizeof运算符返回类型size_t类型,表示函数time()的返回值类型time_t类型。C标准规定sizeof和time()应返回整数类型,但它留给具体的实现来决定到底是哪种整数类型。
time_t time(time_t *);
使用#define可以实现typedef的一部分功能。如下:
#define BYTE unsigned char
但也有#define实现不了的功能。如下:
typedef char * STRING;
如果没有关键字typedef,该例将STRING识别为一个char指针。有了这个关键字,是STRING成为char指针的标识符。
但是假设这样做:
#define STRING char *
那么:
STRING name, sign;
将会被翻译为下面的形式:
char * name, sign; // 只有name是一个指针
也可以对结构使用typedef:
typedef struct complex {
float real;
float imag;
} COMPLEX;
这样就可以用类型COMPLEX代替struct complex来表示复数。使用typedef来命名一个结构类型时,可以省去结构的标记:
tyoedef struct {double x; double y;} rect;
可以像下面这样使用typedef定义的类型名:
rect r1 = {3.0, 6.0};
rect r2;
r2 = r1;
↓相当于
struct {double x; double y;} r1 = {3.0, 6.0};
struct {double x; double y;} r2;
r2 = r1;
使用typedef的原因:
- 为经常出现的类型创建一个方便的、可识别的名称
- 经常被用于复杂的类型
C Primer Plus之结构和其他数据形式的更多相关文章
- C Primer Plus学习笔记(十三)- 结构和其他数据形式
建立结构声明 结构声明(structure declaration)描述了一个结构的组织布局 struct book { char title[MAXTITL]; char author[MAXAUT ...
- 第 14 章 结构和其他数据形式(伸缩型数组成员C99)
伸缩型数组成员C99 声明一个伸缩型数组成员的规则: 1.伸缩型数组成员必须是结构的最后一个成员: 2.结构中必须至少有一个成员: 3.伸缩数组的方括号是空的. 示例 struct flex { in ...
- 第 14 章 结构和其他数据形式(names)
*--------------------------------- names1.c -- 使用指向结构的指针 ---------------------------------*/ #includ ...
- 第 14 章 结构和其他数据形式(enum枚举)
/*----------------------------- enum.c -- 使用枚举类型的值 -----------------------------*/ #include <stdi ...
- 第 14 章 结构和其他数据形式(names3)
/*----------------------------------- names3.c -- 使用指针和 malloc() ----------------------------------- ...
- SQL复制表结构或表数据
需求: 软件开发过程中,一般会部署两个数据库:一个测试数据库提供给开发和测试过程使用:一个运维数据库提供上线使用.当需求变化需增加表时,会遇到数据库表结构或表数据同步的问题,这时就要复制表结构或表数据 ...
- mysql 导出表结构和表数据 mysqldump用法
mysql 导出表结构和表数据 mysqldump用法 命令行下具体用法如下: mysqldump -u用戶名 -p密码 -d 数据库名 表名 > 脚本名; 导出整个数据库结构和数据mysq ...
- 淘宝杨志丰:OceanBase--淘宝结构化大数据解决之道
时至今日,“Big data”(大数据)时代的来临已经毋庸置疑,尤其是在电信.金融等行业,几乎已经到了“数据就是业务本身”的地步.这种趋势已经让很多相信数据之力量的企业做出改变.恰逢此时,为了让更多的 ...
- 【MySQL】MySQL复制表结构、表数据
平常,复制.备份表,一般都直接操作IDE完成.但有时,一些初始化数据的脚本,在操作数据前,最好备份下操作表的结构.数据,不至于出错了被置于为难的境地. 所以复制表结构.表数据的语句就派上用场. > ...
随机推荐
- iOS学习之C语言数组
一.一维数组 数组:具有相同类型的成员组成的一组数据 1.定义 元素:数组中存放的数据成为数组的元素 数组是构造类型 用{}来给构造类型赋初始值 类型修 ...
- MYSQL主键存在则更新,不存在则插入的解决方案(ON DUPLICATE KEY UPDATE)
经常我们使用的最简单的数据库操作就是数据的更新,删除和插入,对于批量删除和插入的方法相信大家都很清楚,那么批量更新估计有的人就不知道了,并且还有批量插入,在插入时若有主键冲突则更新的操作,这在EAV模 ...
- 浅谈.NET中闭包
什么是闭包 闭包可以从而三个维度来说明.在编程语言领域,闭包是指由函数以及与函数相关的上下文环境组合而成的实体.通过闭包,函数与其上下文变量之间建立起关联关系,上下文变量的状态可以在函数的多次调用过程 ...
- asf与vga视频为何无法同步播放?我来给你解释!
因为你的笔记本可以,所以你的台式机不可以,当然跟深刻的原因是,你的笔记本有什么特别硬件
- [SSH服务]——一个SSH无密码登陆实验
实验拓扑图 实验描述 机房内有两台服务器: (1)B服务器10.0.10.158,充当Web服务器,有普通用户user_00 (2)C服务器10.0.10.191,充当Mysql服务器,有普通用户us ...
- android中 回调方法,怎么转变为阻塞执行的方法
项目中需要用到在wifi这一块,扫描附近wifi,然后一个个遍历所有wifi,并且尝试连接,所以就在这里需要每连接一个wifi进行阻塞,当连接失败的时候才进行尝试下一个连接,当连接成功时则break. ...
- 记codevs第一次月赛
第一次参加这种有奖励的比赛(没错,我就是为猴子而去的 一年没怎么碰代码果然手生,还是用没写多久的C++,差点全跪了 T1数学奇才琪露诺: 首先定义一个函数F(x),F(x)=x的各个数位上的数字和 然 ...
- bzoj 3295 树套树
比较裸,可以有好多的优化,比如根本没有删除的点没有加在树套树中的必要,预处理 出来每个不会被删除的值可以减少不少时间,也可以写成树状数组套平衡树,都会快很多 /******************** ...
- 【BZOJ】【3676】【APIO2014】回文串
回文自动机/Manacher+SA 这道题可以用Manacher找出本质不同的回文串(令max增大的所有回文串),然后再用SA跑出来有多少相同. 还有一种做法就是回文自动机(Orz Hzwer)的裸题 ...
- jquery cdn加速点
新浪jquery cdn加速点: <script src="http://lib.sinaapp.com/js/jquery/1.7.2/jquery.js">< ...