zoukankan      html  css  js  c++  java
  • C Primer Plus学习笔记(十三)- 结构和其他数据形式

    建立结构声明

    结构声明(structure declaration)描述了一个结构的组织布局

    struct book
    {
    	char title[MAXTITL];
    	char author[MAXAUTL];
    	float value;
    };
    

    该声明描述了一个由两个字符数组和一个 float 类型变量组成的结构。该声明并未创建实际的数据对象,只描述了该对象由什么组成

    关键字 struct,它表明跟在其后的是一个结构,后面是一个可选的标记(该例中是 book),程序中可以使用该标记引用该结构

    在结构声明中,用一对花括号括起来的是结构成员列表。

    每个成员都用自己的声明来描述,例如,title 部分是一个内含 MAXTITL 个元素的 char 类型数组

    成员可以是任意一种 C 的数据结构,甚至可以是其他结构

    右花括号后面的分号是声明所必需的,表示结构布局定义结束

    struct book library;
    

    这把 library 声明为一个使用 book 结构布局的结构变量

    可以把这个声明放在所有函数的外部,也可以放在一个函数定义的内部

    如果把结构声明置于一个函数的内部,它的标记就只限于该函数内部使用

    如果把结构声明置于函数的外部,声明结构之后的所有函数都能使用它的标记

    例如,在程序的另一个函数中,可以这样声明

    struct book dickens;
    

    该函数便创建了一个结构变量 dickens,该变量的结构布局是 book

    结构的标记名是可选的

    定义结构变量

    结构有两层含义

    一层含义是“结构布局”,结构布局告诉编译器如何表示数据,但是它并未让编译器为数据分配空间

    另一层含义是创建一个结构变量

    struct book library;
    

    编译器执行这行代码便创建了一个结构变量 library,编译器使用 book 模板为该变量分配空间

    在结构变量的声明中,struct book 所起的作用相当于一般声明中的 int 或 float

    struct book library;
    
    相当于
    
    struct book
    {
    	char title[MAXTITL];
    	char author[MAXAUTL];
    	float value;
    } library;  // 声明的右花括号后跟变量名
    

    声明结构的过程和定义结构变量的过程可以组合成一个步骤

    组合后的结构声明和结构变量定义不需要使用结构标记

    struct // 无结构标记
    {
    	char title[MAXTITL];
    	char author[MAXAUTL];
    	float value;
    } library;
    

    初始化结构

    初始化一个结构变量(ANSI 之前,不能用自动变量初始化结构,ANSI 之后可以用任意存储类别)与初始化数组的语法类似

    struct book library = {
    	"Hello World",
    	"Jack",
    	22.1
    };
    

    使用在一对花括号中括起来的初始化列表进行初始化,各初始化项用逗号分隔

    如果初始化静态存储期的变量(如,静态外部链接、静态内部链接或静态无链接),必须使用常量值,这同样适用于结构。如果初始化一个静态存储期的结构,初始化列表中的值必须是常量表达式。如果是自动存储期,初始化列表中的值可以不是常量

    访问结构成员

    使用结构成员运算符——点(.)访问结构中的成员

    例如,library.value 即访问 library 的 value 部分。虽然 library 是一个结构,但是 library.value 是一个 float 类型的变量,可以像使用其他 float 类型变量那样使用它

    . 比 & 的优先级高

    结构的初始化器

    C99 和 C11 为结构提供了指定初始化器(designated initializer),其语法与数组的指定初始化器类似

    结构的指定初始化器使用点运算符和成员名(而不是方括号和下标)标识特定的元素

    例如,只初始化 book 结构 value 成员

    struct book library = {.value = 10.99};
    

    可以按照任意顺序使用指定初始化器

    struct book library = {
    	.title = "Hello World",
    	.value = 10.99,
    	.author = "Jack"
    };
    

    与数组类似,在指定初始化器后面的普通初始化器,为指定成员后面的成员提供初始值

    对特定成员的最后一次赋值才是它实际获得的值

    struct book library = {
    	.value = 10.99,
    	.author = "Jack",
    	22.55
    };
    

    value 最后的值为 22.55

    结构数组

    声明结构数组

    声明结构数组和声明其它类型的数组类似

    struct book library[MAXBKS];
    

    把 library 声明为一个内含 MAXBKS 个元素的数组,数组的每个元素都是一个 book 类型的数组

    数组名 library 本身不是结构名,它是一个数组名,该数组中的每个元素都是 struct book 类型的结构变量

    标识结构数组的成员

    标识结构数组中的成员,可以采用访问单独结构的规则:在结构名后面加一个点运算符,再在点运算符后面写上成员名

    library[0].value  // 第 1 个数组元素与 value 相关联
    library[4].title  // 第 5 个数组元素与 title 相关联
    

    数组下标是在 library 后面,不是在成员名后面

    使用 library[2].value 的原因是,library[2] 是结构变量名

    library[2].title[4] 的意思是,library 数组第 3 个结构变量中 title 的第 5 个字符

    总结一下:

    library              // 一个 book 结构的数组
    library[2]           // 一个数组元素,该元素是 book 结构
    library[2].title     // 一个 char 数组(library[2] 的 title 成员)
    library[2].title[4]  // 数组中 library[2] 元素的 title 成员的第 5 个字符
    

    嵌套结构

    在一个结构中包含另一个结构

    #include <stdio.h>
    #define LEN 20
    
    const char * msgs[5] = {
    	"  Thank you for the wonderful evening, ",
    	"You certainly prove that a ",
    	"is a special kind of guy. We must get together",
    	"over a delicious ",
    	" and have a few laughs"
    };
    
    struct names {                // 第 1 个结构
    	char first[LEN];
    	char last[LEN];
    };
    
    struct guy {                  // 第 2 个结构
    	struct names handle;      // 嵌套结构
    	char favfood[LEN];
    	char job[LEN];
    	float income;
    };
    
    int main(void){
    	struct guy fellow = {
    		{"Ewen", "Villard"},
    		"grilled salmon",
    		"personality coach",
    		68112.00
    	};
    
    	printf("Dear %s, 
    
    ", fellow.handle.first);
    	printf("%s%s.
    ", msgs[0], fellow.handle.first);
    	printf("%s%s
    ", msgs[1], fellow.job);
    	printf("%s
    ", msgs[2]);
    	printf("%s%s%s
    ", msgs[3], fellow.favfood, msgs[4]);
    	if (fellow.income > 150000.0)
    		puts("!!");
    	else if (fellow.income > 75000.0)
    		puts("!");
    	else
    		puts(".");
    	printf("
    %40s%s
    ", " ", "See you soon,");
    	printf("%40s%s
    ", " ", "Shalala");
    
    	return 0;
    }
    

    运行结果

    访问嵌套结构的成员,需要使用两次点运算符

    指向结构的指针

    就像指向数组的指针比数组本身更容易操纵(如,排序问题)一样,指向结构的指针通常比结构本身更容易操纵

    在一些早期的 C 实现中,结构不能作为参数传递给函数,但是可以传递指向其他结构的指针

    即使能传递一个结构,传递指针通常更有效率

    一些用于表示数据的结构中包含指向其他结构的指针

    #include <stdio.h>
    #define LEN 20
    
    struct names {
    	char first[LEN];
    	char last[LEN];
    };
    
    struct guy {
    	struct names handle;
    	char favfood[LEN];
    	char job[LEN];
    	float income;
    };
    
    int main(void){
    	struct guy fellow[2] = {
    		{
    			{"Eween", "Villard"},
    			"grilled salmon",
    			"personality coach",
    			68112.00
    		},
    		{
    			{"Rodeny", "Swillbelly"},
    			"tripe",
    			"tabloid editor",
    			432400.00
    		}
    	};
    	struct guy * him;  // 这是一个指向结构的指针
    
    	printf("address #1: %p  #2: %p
    ", &fellow[0], &fellow[1]);
    	him = &fellow[0];  // 告诉编译器指针该指向何处
    	printf("pointer #1: %p  #2: %p
    ", him, him + 1);
    	printf("him->income is $%.2f: (*him).income is $%.2f
    ",
    		him->income, (*him).income);
    	him++;             // 指向下一个结构
    	printf("him->favfood is %s: him->handle.last is %s
    ",
    		him->favfood, him->handle.last);
    
    	return 0;
    }
    

    运行结果

    声明和初始化结构指针

    struct guy * him;
    

    首先是关键字 struct,其次是结构标记 guy,然后是一个星号(*),其后跟着指针名

    该声明并未创建一个新的结构,但是指针 him 现在可以指向任意现有的 guy 类型的结构

    例如,如果 barney 是一个 guy 类型的结构变量,可以这样写

    him = &barney;
    

    结构变量名不是结构的地址,因此要在结构变量名前面加上 & 运算符

    在上面代码中 fellow 是一个结构数组,这意味着 fellow[0] 是一个结构。要让 him 指向 fellow[0],可以这样写:

    him = &fellow[0];
    

    him 指向 fellow[0],him + 1 指向 fellow[1]

    him 加 1相当于 him 指向的地址加 84

    在十六进制中,874 - 820 = 54(十六进制)= 84(十进制)

    因为每个 guy 结构都占用 84 字节的内存:name.first 占用 20 字节,name.last 占用 20 字节,favfood 占用 20 字节,job 占用 20 字节,income 占用 4 字节(float 占用 4 字节)

    用指针访问成员

    第 1 种方法是:使用 -> 运算符,该运算符由一个连接号(-)后跟一个大于号(>)组成

    如果 him == &barney,那么 him->income 即是 barney.income
    
    如果 him == &fellow[0],那么 him->income 即是 fellow[0].income
    

    第 2 种方法是:以这样的顺序指定结构成员的值,如果 him == &fellow[0],那么 *him == fellow[0],因为 & 和 * 是一对互逆运算符

    fellow[0].income == (*him).income
    

    必须要使用圆括号,因为 . 运算符比 * 运算符的优先级高

    如果 him 是指向 guy 类型结构 barney 的指针,下面的关系恒成立

    barney.income == (*him).income == him->income  // 假设 him == &barney
    
  • 相关阅读:
    HSF原理
    Spring IOC 容器源码分析
    Spring Bean注册和加载
    CAP和BASE理论
    Java内存模型
    Java线程模型
    IO复用、多进程和多线程三种并发编程模型
    无锁编程本质论
    An Introduction to Lock-Free Programming
    安装与配置ironic
  • 原文地址:https://www.cnblogs.com/sch01ar/p/9347434.html
Copyright © 2011-2022 走看看