《C程序设计语言》笔记 (六) 结构

6.1结构的基本知识

结构是一个或多个变量的集合,这些变量可能为不同的类型,为了处理的方便而将这些变量组织在一个名字之下

结构可以拷贝、赋值、传递给函数,函数也可以返回结构类型的返回值

自动结构和数组现在也可以进行初始化

struct point{
  int x;
  int y;
}

关键字struct引入结构声明

结构声明由包含在花括号内的一系列声明组成

关键字struct后面的名字是可选的,称为结构标记

结构标记用于为结构命名,在定义结构后,结构标记就代表花括号内的声明,可以用它作为声明的简写形式

结构中定义的变量称为成员

结构成员、结构标记、和普通变量可以采用相同的名字,不会冲突

struct声明定义了一种数据类型

在标记结构成员结束的右花括号之后可以跟一个变量表

struct{ ... } x,y,z;
这和
int x,y,z
具有类似的意义,这两个声明都将x、y与z声明为指定类型的变量,并分配存储空间

如果结构声明后面不带变量表,则不需要为它分配存储空间,它仅仅描述了一个结构的模板

如果结构声明中带有标记,那么在以后定义结构实列时便可以使用该标记定义

struct point pt;

在表达式中可以通过下列形式引用某个特定的结构中的成员
结构名.成员

结构可以嵌套

  

6.2结构与函数

结构的合法操作只有几种:

作为一个整体复制和赋值
通过&运算符去 地址,访问其成员
复制和赋值包括向函数传递参数以及从函数返回值
结构之间不可以进行比较
可以用一个常量成员值列表初始化结构
自动结构也可以通过赋值初始化

如果传递给是的结构很大,使用指针的效率通常比复制整个结构的效率高

结构指针类似于普通变量指针 声明
struct point *pp;

将定义一个指向struct point类型的指针

如果pp指向一个point结构,那么*pp即为该结构
而*(pp).x和*(pp).y则是结构成员

*(pp).x中圆括号是必须的,因为结构成员运算符"." 的优先级比"*"的优先级高
表达式 *pp.x含义等于 *(pp.x) ,x不是指针,所以是非法的

结构指针的使用频率非常高,为了方便,C语言提供另一种简写
p->结构成员

在所有的运算符中4个优先级最高:
结构运算符. 和->
函数调用的()
已经下标[] 

表达式  ++pt->len
将增加len的值,而不是增加pt的值,其中隐含的括号关系 ++(pt->len)

  

6.3结构数组

数组的长度在编译时已经完全确定,它等于数组项的长度乘以项数

C语言提供了一个编译时计算任一对象的长度的运算符 sizeof

表达式 
    sizeof 对象
或  
    sizeof(类型名)
将返回一个整形值,他等于指定对象或类型占用的空间字节数

严格来说sizeof返回的是无符号整形,其类型为size_t

条件编译语句#if中不能使用sizeof,因为预处理器不对类型名进行分析

但预处理器并不计算#define语句中的表达式,因此 #define中使用sizeof是合法的

  

6.4指向结构的指针

千万不要认为结构的长度等于各成员长度的和。因为不同的对象有不同的对齐要求。
所以结构中可能会出现未命名的"空穴"(hole)

例如:
 
struct{
   char c;
   int i; 
} 

可能需要8字节的存储空间,而不是5字节。使用sizeof可以返回正确的对象长度

  

6.5自引用结构

struct tnode{
   char *word;
   int count;
   struct tnode *left;
   struct tnode *right;
}

一个包含其自身实列的结构是非法的
但是,下列声明是合法的

struct tnode *left;
它将left声明为指向tnode的指针,而不是tnode本身

  

6.6表查找

散列查找法: 将输入的名字转换为一个小的非负整数,该整数随后将作为一个指针数组的下标
数组的每个元素指向某个链表的表头,链表中的各个块用于描述具有该散列值的名字,如果没有名字散列到该值,则数组元素的值为NULL

unsigned hash(char *s){
  unsigned hashval;
    for(hashval=0; *s != '\0';s++)
    hashval = *s+31*hashval;
    return hashval % HASHSIZE;
}

由于在散列计算时采用的是无符号算术运算,因此保证了散列值非负

  

6.7类型定义

C语言提供了一个typedef的功能,用来建立新的数据类型名
例如:
typedef int Length;
将Length 定义为何int具有同等意义的名字。
类型Length可用于类型什么、类型转换等

类似于
typedef char *String
将String定义为和char *或字符指针同义,此后,便可以在类型声明和类型转换中使用String

typedef中声明的类型在变量名的位置出现,而不是紧接着typedef之后

从任何意义上讲,typedef声明并没有创建一个新类型,它只是为某个已存在的类型增加了一个新的名字

实际上typedef类似于#define语句,但由于typedef是编译器解释的,因此他的文本替换功能要超过预处理的能力

除了表达式更简洁外,typedef使用还有两个重要原因

首先它可以是程序参数化,以提高程序的可移植性
第二是一位程序提供更好的说明性---Treeptr类型显然比一个声明为指向复杂结构的指针更容易让人理解

  

6.8联合

联合是可以在不同时刻保存不同类型和长度的对象的变量,编译器负责跟踪对象的长度和对齐要求。

联合提供了一种方式,在单块存储区中管理不同类型的数据,而不需要程序中嵌入任何机器有关的信息

例如:
union u_tag{
   int ival;
   float fval;
   char *sval;
    
} u;

变量u必须足够大,以保存3种类型中最大的一种,具体长度同具体的实现有关

这些类型中的任何一种类型的对象都可以赋值给u,且可使用在随后的表达式中,但必须保证是一致的:
读取的类型必须是最近一次存入的类型。

程序员负责跟踪当前保存在联合中的类型

如果保存的类型和读取的类型不一致,其结果取决于具体的实现

联合可以使用在结构和数组中,反之亦可


实际上,联合就是一个结构,他的所有成员相对于基地址偏移量都为0

此结构空间要大到足够容纳最宽的成员。

并且,其对齐方式要适合联合中所有的类型的成员。

对联合允许的操作与对结构允许的操作相同:

作为一个整体单元进行复制 赋值 取地址及访问成员

联合只能用第一个成员类型的值进行初始化,因此,联合u只能用整数进行初始化

  

  

6.9 位字段

在存储空间很宝贵的情况下,有可能需要将多个对象保存在一个机器字中。一种常用的方法是,使用类似于编译器符号表的单个二进制位标志集合。

外部强加的数据格式也经常需要从字的部分位中读取数据

考虑编译器中符号表操作的有关细节

程序中的每个标识符都有与之相关的特定信息

C语言提供了一种直接定义和访问一个字中的位字段的能力,而不需要通过按位操作

位字段(bit-field)或简称字段,是字中相邻位的集合

某些机器上字段的分配是从字的左端至右端进行的,而某些机器相反。

  

原文地址:https://www.cnblogs.com/dafume/p/7803519.html