声明和初始化结构指针

声明结构化指针,例如:

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具有一个称为伸缩型数组成员的新特性。利用这一新特性可以声明最后一个成员是一个具有特殊属性的数组的结构。该数组成员的特殊属性之一是它不存在,至少不立即存在。第二个特殊属性是您可以编写适当的代码使用这个伸缩型数组成员,就像它确实存在并且拥有您需要的任何数目的元素一样。

声明一个伸缩型数组成员的规则:

  1. 伸缩型数组成员必须是最后一个数组成员
  2. 结构中必须至少有一个其他成员
  3. 伸缩型数组就像普通数组一样被声明,除了它的方括号内是空的

例如:

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种选择:

  1. 把一个联合初始化为同样类型的一个联合
  2. 初始化联合第一个元素
  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

使用联合的适当场合:

  1. 使用一个成员来将值保存到一个联合中,该结构所存储的信息有赖于其中的一个成员。
  2. 在某些结构中,该结构所存储的信息有赖于其中的一个成员。

枚举类型

可以使用枚举类型声明代表整数常量的符号名称。通过使用关键字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个不同之处:

  1. 与#define不同,typedef给出的符号名称仅限于对类型,而不是对值
  2. typedef的解释由编译器,而不是预处理器
  3. 虽然它的范围有限,但在其受限范围内,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的原因:

  1. 为经常出现的类型创建一个方便的、可识别的名称
  2. 经常被用于复杂的类型

C Primer Plus之结构和其他数据形式的更多相关文章

  1. C Primer Plus学习笔记(十三)- 结构和其他数据形式

    建立结构声明 结构声明(structure declaration)描述了一个结构的组织布局 struct book { char title[MAXTITL]; char author[MAXAUT ...

  2. 第 14 章 结构和其他数据形式(伸缩型数组成员C99)

    伸缩型数组成员C99 声明一个伸缩型数组成员的规则: 1.伸缩型数组成员必须是结构的最后一个成员: 2.结构中必须至少有一个成员: 3.伸缩数组的方括号是空的. 示例 struct flex { in ...

  3. 第 14 章 结构和其他数据形式(names)

    *--------------------------------- names1.c -- 使用指向结构的指针 ---------------------------------*/ #includ ...

  4. 第 14 章 结构和其他数据形式(enum枚举)

    /*----------------------------- enum.c -- 使用枚举类型的值 -----------------------------*/ #include <stdi ...

  5. 第 14 章 结构和其他数据形式(names3)

    /*----------------------------------- names3.c -- 使用指针和 malloc() ----------------------------------- ...

  6. SQL复制表结构或表数据

    需求: 软件开发过程中,一般会部署两个数据库:一个测试数据库提供给开发和测试过程使用:一个运维数据库提供上线使用.当需求变化需增加表时,会遇到数据库表结构或表数据同步的问题,这时就要复制表结构或表数据 ...

  7. mysql 导出表结构和表数据 mysqldump用法

    mysql 导出表结构和表数据 mysqldump用法 命令行下具体用法如下:   mysqldump -u用戶名 -p密码 -d 数据库名 表名 > 脚本名; 导出整个数据库结构和数据mysq ...

  8. 淘宝杨志丰:OceanBase--淘宝结构化大数据解决之道

    时至今日,“Big data”(大数据)时代的来临已经毋庸置疑,尤其是在电信.金融等行业,几乎已经到了“数据就是业务本身”的地步.这种趋势已经让很多相信数据之力量的企业做出改变.恰逢此时,为了让更多的 ...

  9. 【MySQL】MySQL复制表结构、表数据

    平常,复制.备份表,一般都直接操作IDE完成.但有时,一些初始化数据的脚本,在操作数据前,最好备份下操作表的结构.数据,不至于出错了被置于为难的境地. 所以复制表结构.表数据的语句就派上用场. > ...

随机推荐

  1. 代码文档生成工具-Doxygen生成CHM和RTF图文教程

    Doxygen是一种开源跨平台的,以类似JavaDoc风格描述的文档系统,可以从一套归档源文件开始,生成chm格式的文档.本文主要讲解如何在winddows下安装doxygen.     1.下载do ...

  2. C#全局作用符::

    比如说你在全局定义了一个变量str,然后在函数里面又定义了这个str名字的变量的,这个时候你要是在函数里面直接写str,那么就是访问的函数内部的变量的.无法访问外部变量的.这是正常的现象的.但是如果你 ...

  3. PB中multieditline空间的“~r~n"转"~n"

    private: constant String MULEDIT_NEWLINE = "~r~n" //multilineEdit控件的换行符号 constant String M ...

  4. P1297: [SCOI2009]迷路

    首先知道,如果没有路径长度的要求,且给定的邻接矩阵只有0和1表示通与不通的话,从S->E走N次的方案数就是这个矩阵自乘N次后的(S,E)的数值.这样的话只需要快速幂+矩阵乘法即可过关. (转载请 ...

  5. [转载]求平方根sqrt()函数的底层算法效率问题

    我们平时经常会有一些数据运算的操作,需要调用sqrt,exp,abs等函数,那么时候你有没有想过:这个些函数系统是如何实现的?就拿最常用的sqrt函数来说吧,系统怎么来实现这个经常调用的函数呢? 虽然 ...

  6. 如何在Report Builder中使用fnd_profile.value

    在EBS的Report开发中,需要根据客户化的一个Profile来控制用户可以访问的数据,可是在开发的过程中发现一直取不到该Profile的值,后来百度才找到了原因. 解决方法: 1.添加用户参数p_ ...

  7. 20145120 《Java程序设计》第8周学习总结

    20145120 <Java程序设计>第8周学习总结 教材学习内容总结 NIO使用频道(channel)来衔接数据节点 read()将ReadableByteChannel中的数据读至By ...

  8. Notes of the scrum meeting(12.5)

    meeting time:18:00~18:30p.m.,December 5th,2013 meeting place:3号公寓一层 attendees: 顾育豪                   ...

  9. Shell常用操作

    1.读取配置文件中的jdbc_url参数的值($InputParamFile为待读取的目标文件绝对路径) jdbc_url=`grep "jdbc_url" $InputParam ...

  10. Netsharp快速入门(之7) 基础档案(工作区1 向导创建工作区)

    作者:秋时 杨昶   时间:2014-02-15  转载须说明出处 3.5     商品开发 3.5.1  创建部件工作区 3.5.1.1 工作区向导 1.打开平台工具,选择界面管理节点下的部件工作区 ...