1、结构的基本知识
关键字struct引入结构声明,结构声明由包含在花括号内的一系列声明组成,关键字struct后面的名字是可选的,成为结构标记。结构成员、结构标记和普通变量(即非成员)可以采用相同的名字。
2、结构与函数
结构的合法操作只有几种:作为一个整体复制和赋值,通过&运算符取地址,访问其成员。
在所有运算符中,下面4个运算符的优先级最高(C++中作用域解析运算符优先级最高,其次是这4个):结构运算符“.”和“->”、用于函数调用的“()”以及用于下标的“[]”。这里举一个例子:假设p是结构体类型的指针变量,str是其中的指针类型的成员,那么*p+±>str先读取指针str指向的对象的值,然后再将p加1。首先结合的是->,其次是++,最后是*。但++在这是后缀,所以是先执行,再加1。
3、结构数组
这里先提一下本节中介绍的sizeof。
文中说的是“编译时(compile-time)一元运算符”,可以用来计算任一对象的长度。看了一下网上的说明,sizeof既是一个运算符,也是一个关键字,或者说是一个非常特殊的,能作为运算符的关键字。严格地说,sizeof的返回值是无符号整型值,其类型为size_t(可以参考下https://www.zhihu.com/question/24773728),该类型在头文件<stddef.h>中定义。
文中有句话:“条件编译语句#if中不能使用sizeof,因为预处理器不对类型名进行分析,但预处理器并不计算#define语句中的表达式,因此,在#define中使用sizeof是合法的。”我想它大概意思就是,预编译(预处理)在编译过程的早期进行, 此时尚未对类型名称进行分析,所以不能理解sizeof要处理的类型名,而define只是文本替换,所以无所谓?
这里针对结构数组举的程序例子是统计输入中各个C语言关键字出现的次数。
这里使用结构数组,数组中的每个结构元素包括一对变量。声明为
struct key{
char *word;//用来存放关键字名
int count;//用来存放相应关键字的出现次数
} keytab[NKEYS];
初始化的方式为
struct key{
char *word;
int count;
} keytab[] = {
{
"auto", 0},
{
"break", 0},
{
"case", 0},
/* ... */
{
"while", 0}
};
这是比较精确的做法,如果初值是简单变量或字符串,并且其中的任何值都不为空,则内层的花括号可以省略。
通过这一节的这个程序再次对getchar这个函数的机制充满了疑问。?
4、指向结构的指针
这里再次用到了5.4节中允许指向相同数组中元素的两个指针进行减法运算,以及C保证数组末尾之后第一个元素的指针运算符可以正确运行这两个规则,来将上一节中的程序改成使用结构的指针来完成。
有一个需要注意的点:结构的长度可能不等于各成员长度的和。
网上一个说法是:
- C语言的编译过程是从上到下的。
- 结构体某成员的地址偏移量必须为该成员的整数倍,如果不是整数倍,则前面成员变量需要填充。
- 结构体最后一个成员所占字节的长度=max(所有的成员长度)。
在这里,某成员的地址偏移量,应该就是它前面的成员已经占据的长度。那么,如果第一个成员是char,第二个是int,如果第一个成员占据一个字节长度,那第二个int型成员的地址偏移量将会是1,显然不是int类型长度的整数倍,因此第一个成员需要填充相应的的字节。以此类推,后面的成员也要满足此条件。这就是书上提到的“空穴”(hole)。当然使用sizeof可以得到正确的对象长度。
5、自引用结构
给出了二叉树的实现,以统计输入中所有单词的出现次数。
首先是树的定义:
struct tnode {
/* the tree node: */
char *word; /* points to the text */
int count; /* number of occurrences */
struct tnode *left; /* left child */
struct tnode *right