C和指针读书笔记(第十章)

1. 结构基础知识

  • 结构是一些值的集合,这些值称为它的成员。但一个结构的各个成员可能有不同的类型。
  • 结构成员都有自己的名字,是通过名字来访问的。
  • 结构变量和数组不同,在表达式中使用时,它并不被替换成一个指针。
  • 结构变量属于标量类型,可以作为传递给函数的参数,也可以作为返回值从函数返回,相同类型的结构变量之间可以相互赋值,你可以声明指向结构的指针,取结构变量的地址,也可以声明结构数组。

2. 结构声明
声明结构时,必须列出它所包含的所有成员。这个列表包括所有成员的名字和类型。

Struct tag{
    member-list
}variable list ;

结构声明的语法需要做一些解释。所有可选部分不能全部省略。

//这个声明创建了一个名叫x的变量,包含三个成员:一个整数,一个字符和一个浮点数
struct{
    int a;
    char b;
    float c;
}x;
//这个声明创建了y和z。y是一个数组,它包含了20个结构。z是一个指针,它指向这个类型的结构。
struct{
    int a;
    char b;
    float c;
}y[20], *z;

即使成员列表相同,但变量y和z的类型完全不同。
标签(tag)字段允许为成员列表提供一个名字,这样它就可以在后续的声明列表中使用。标签允许多个列表使用同一个成员列表,并且创建同一种类型的结构。

//这个声明把标签SIMPLE和这个成员列表联合在一起
//该申明并没有提供变量列表,所以并未创建任何变量
struct SIMPLE{
    int a;
    char b;
    float c;
};
//标签声明了一种模式,用于声明未来的变量
//这些声明使用标签来创建变量
struct SIMPLE X;
STRUCT SIMPLE y[20], *z;
//现在x,y和z是同一种类型的结构变量

//申明结构时可以使用的另一种方法是使用typedef创建一个新的类型。
typedef struct{
    int a;
    char b;
    float c;
}Simple;
//这个方法和声明一个结构标签的效果几乎相同
//区别在于Simple现在是一个类型名而不是结构标签
//后续声明
Simple x;
Simple y[20], *z;

3. 结构成员
可以在一个结构外部声明的任何变量都可以作为结构的成员。结构成员可以是标量,数组,指针甚至其他结构。
一个结构的成员名可以和其他结构的成员名相同,所以这个结构的成员a并不会和struct SIMPLE s的成员a冲突。

struct COMPLEX{
    float f;
    int a[20];
    long *lp;
    struct SIMPLE s;
    struct SIMPLE sa[10];
    struct SIMPLE *sp;
};

4. 结构成员的直接访问
通过点操作符(.)完成访问。点操作符接受两个操作数,左操作数是结构变量的名字,右操作数是需要访问的成员的名字。表达式的结果就是指定的成员。

struct COMPLEX{
    float f;
    int a[20];
    long *lp;
    struct SIMPLE s;
    struct SIMPLE sa[10];
    struct SIMPLE *sp;
};

struct COMPLEX comp;

comp.a是一个数组名。comp.s是一个结构名。(comp.s).a选择结构comp的成员s的一个成员a,结果是一个成员,和comp.s.a等价。
comp.sa是一个结构数组名,它的值是一个指针常量。对这个表达式使用下标引用将选择一个数组元素,且这个元素本身是一个结构,所以可以用另一个点操作符取得它的成员之一。如((comp.sa)[4]).c,等价于comp.sa[4].c。


5. 结构成员的间接访问
->操作符(箭头操作符)可以完成间接访问操作,箭头操作符接受两个操作数,左操作数必须是一个指向结构的指针 ,箭头操作符对左操作数执行间接访问取得指针所指向的结构,然后和点操作符一样,根据右操作数选择一个指定的结构成员。
当知道指向结构的指针(*p)但不知道结构的名字(STR)时,便可以使用表达式p->a,当知道结构的名字,便可以使用STR.a。
例:

void func(struct COMPLEX *cp);
(*cp).f
cp->f
//这两种写法功能一致

6. 结构的内部不能包含一个类型为该结构本身的成员,但是结构的内部可以包含一个指向该结构本身的指针。
事实上,指针指向的是同一种类型的不同结构

//非法
struct SELF_REF1{
        int a;
        struct SELF_REF1 b;
        int c;
};
//合法
struct SELF_REF2{
        int a;
        struct SELF_REF2 *b;
        int c;
};
typedef struct{
        int a;
        SELF_REF3 *b;
        int c;
}SELF_REF3;
//非法,类型名知道声明的末尾才定义,故在结构声明的内部它未定义
//定义一个结构标签来声明b即可
typedef struct SELF_REF3_TAG{
        int a;
        struct SELF_REF3_TAG *b;
        int c;
}SELF_REF3;

7. 为了声明两个结构,且每个结构都包含一个指向对方的指针的成员,我们需要使用不完整的声明来定义一个结构标签名。结构变量可以用一个花括号包围的值的列表进行初始化,这些值的类型必须适合它所初始化的那些成员。

struct B;
stryct A{
    struct B *p;
};
stryct B{
    struct A *p;
};
//在A的成员列表中需要标签B的不完整的声明,一旦A被声明,B的成员列表也可以被声明

8. 结构的初始化右一个位于一对花括号内部、由逗号分隔的初始值列表可用于结构各个成员的初始化。这些值根据结构成员列表的顺序写出,出事列表值不够,剩余的结构成员将使用缺省值进行初始化。


9. 结构,指针和成员 P200~205


10.

typedef struct{
    int a;
    short b[20];
}Ex;
Ex x;
*px = &x;

因为结构的第1个成员是a,所以a的地址和结构的地址是一样的。所以*px和px->a内部保存的地址的值是相同的,但是它们的类型不同,变量px被声明为一个指向结构的指针,所以表达式*px的结果是整个结构,而px->a的结果是结构的第1个成员。

int *pi;
//如何pi指向整型成员a?
pi = &px->a;//( &px->a等同于&(px->a) )
//->操作符优先级高于&操作符的优先级
//*px的结果是整个结构,*pi的结果是一个单一的整型值

11. 结构的存储分配
编译器为一个结构变量的成员分配内存时要满足它们的边界对齐要求。在实现结构存储的边界对齐时,可能会浪费部分内存空间,根据边界对齐要求降序排列结构成员可以最大限度的减少结构存储中浪费的内存空间。sizeof操作符能得出一个结构的整体长度,包括因边界对齐而跳过的那些字节,故包含了结构中浪费的内存空间。


12. 如果必须确定结构中某个成员的实际位置,应该考虑边界对齐因素,可以使用offsetof宏(定义于stddef.h)

offsetof(type, member)
//type就是结构的类型
//member就是你需要的那个成员名
//表达式结果为一个size_t值,表示这个指定的成员开始存储的位置距离结构开始存储的位置偏移几个字节。

13. 结构可以作为参数传递给函数,也可以作为返回值从函数返回,但是向函数传递一个指向结构的指针效率更高。在结构指针的参数声明上可以加上const关键字防止函数修改指针所指向的结构。
P206~207


14. 位段是结构的一种,但它的成员长度以位为单位指定,位段声明在本质上是不可移植的。位段允许你把长度为奇数的值包装在一起以节省存储空间。

//位段成员必须声明为int,signed int或unsigned int类型
//在成员名的后面是一个冒号和一个整数,这个整数指定该位段所占用的位的数目。
struct char{
    unsigned ch :7;
    unsigned font :6;
    unsigned zie :19;
};

15. 一个联合的所有成员都存储于同一个位置。通过访问不同类型的联合成员,内存中相同的位组合可以被解释为不同的东西。联合比那辆可以进行初始化,但初始值必须与联合的第一个成员的类型匹配。

union{
    float f;
    int i;
}fi;

16. 变体记录 P212~213


2016.10.10

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值