目录
结构体所占存储单元是结构体中各成员所占内存单元的总和,共用体占存储单元的大小是共用体中各成员所占存储单元的最大者。
为什么C语言提供两种命名结构体类型的方法(标记命名和 typedef 命名)?
为什么结构体变量student 在进行了如下定义后进行赋值操作student = {1001, 100,”mike”};会提示出错信息呢?
1、声明结构体类型的同时定义结构体变量,其中如果不指明结构体标签student 也可(即省略student)
如果 char 型的值占有一个字节,而 int 型值占用 4 个字节,s 会是多大呢?
为什么sizeof 返回的值大于结构大小的期望值,是不是尾部有填充?
向函数传递结构体的完整结构和向函数传递结构体的首地址有何不同?
为什么声明结构体类型时可以包含指向本结构体类型的指针成员?(链表)
如何计算结构体类型所占内存的字节数?
- 在计算结构体类型所占内存的字节数时,不能用结构体的每个成员类型所占内存字节数的“和”作为一个结构体实际所占的内存字节数。
- 对多数计算机系统而言,为了提高内存寻址的效率,很多处理器体系结构为特定的数据类型引入了特殊的内存对齐(Memory-Alignment)需求。不同的系统和编译器,内存对齐的方式有所不同,为了满足处理器的对齐要求,可能会在较小的成员后加入补位,从而导致结构体实际所占内存的字节数会比我们想象的多出一些字节。
- 系统为结构体变量分配内存的大小,或者说结构体类型所占内存的字节数,并非是所有成员所占内存字节数的总和,它不仅与所定义的结构体类型有关,还与计算机系统本身有关。
- 由于结构体变量的成员的内存对齐方式和数据类型所占内存的大小都是与机器相关的,因此结构体在内存中所占的字节数也是与机器相关的。所以,计算结构体所占内存的字节数时,一定要使用sizeof 运算符。
结构体所占存储单元是结构体中各成员所占内存单元的总和,共用体占存储单元的大小是共用体中各成员所占存储单元的最大者。
- 结构体和共用体所占存储单元的大小通常意义上可以按照上述描述来理解。
- 实际上,结构体与共用体所占存储单元的大小与机器类型相关,与机器字长、对齐方式alignment,以及 endian 等相关,
- 因此结构体所占存储单元的大小可能并不与结构体各成员所占存储单元的总和严格相等。
抽象数据类型
- 所谓 抽象数据类型 (Abstract Data Type ,ADT )(包括值的集合和作用在值集上操作的集合)
- 是指这样一种数据类型,它不再单纯是一组值的集合,(不仅构造了数据类型还增加了对数据的操作)
- 还包括作用在值集上的操作的集合,
- 即在构造数据类型的基础上增加了对数据的操作,
- 且类型的表示细节及操作的实现细节对外是不可见的。
结构体
- 结构体 (Structure )是将不同类型的数据成员组织到统一的名字之下,适合于对关系紧密、逻辑相关、具有相同或者不同属性的数据进行处理。
- 结构体的名字,称为 结构体标签 (Structure Tag ),作为用户自定义的结构体类型的标志,用于与其他结构体类型相区别。
- 结构体中的各信息项是在结构体标签后面的花括号{和}内声明的。构成结构体的变量,称为结构体成员 (Structure Member )。
typedef
- typedef 语句用于为系统固有的或程序员自定义的数据类型定义一个别名。
- typedef 只是为一种已存在的类型定义一个新的名字而已,并未定义一种新的数据类型。
typedef 能否用#define 代替?
- 不能。typedef 在程序编译时处理,其作用是为一个已经存在的类型申明一个别名,原类型名仍然有效。
- 而#define 在程序预编译时处理,是一种文本的替换操作,宏替换后,被替换的文本不再有效。
- 当然#define也有优点,因为可以在#define 中使用#ifdef。
- 另一方面typedef 具有遵守作用域的规则的优点(可以在函数和块内声明)。
- 由如下例子可以进一步明确两者的区别:
typedef char *string_t;
#define string_d char *;
string_t s1,s2;
string_d s3,s4;
- s1,s2,s3都被定义成char *,但s4 却被定义成 char 型(string_t是类型名,但string_d只是char*的别名是不能对两个变量说明的)
圆点运算符(访问结构体变量成员)
访问结构体变量的成员必须使用 成员选择运算符(也称 圆点运算符)。其访问格式如下:结构体变量名 .成员名
结构体变量与结构体指针对结构体成员的引用方法分别是什么?
- 结构体变量对结构体成员进行引用采用“.域名法”;
- 结构体指针对结构体成员引用采用“->域名法”。
为什么C语言提供两种命名结构体类型的方法(标记命名和 typedef 命名)?
- C语言早期没有 typedef,所以标记是结构类型命名的唯一有效方法。当加入 typedef 时,已经太晚了,以至无法删除标记了。
- 此外,当结构的成员是指向结构自身的指针时,标记始终是非常必要的。
为什么结构体变量student 在进行了如下定义后进行赋值操作student = {1001, 100,”mike”};会提示出错信息呢?
struct student
{
long stuID;
int stuScore;
char stuName[20];
};
- 因为并非是定义了一个结构体变量,而是声明了一个结构体模板,也就是定义了一种称为student 的结构体类型,student 是该种结构体类型的名字,叫做结构体的标签,类似于 int等数据类型名,
- 所以,正如不能给一种类型进行赋值一样,使用 student.stuID 进行各种操作是不正确的。
- 而真正定义一个结构体变量 stu 的方式有如下几种:
1、声明结构体类型的同时定义结构体变量,其中如果不指明结构体标签student 也可(即省略student)
struct student
{
long stuID;
int stuScore;
char stuName[20];
}stu;/*直接在最后写上结构体变量*/
2、声明完结构体类型后再定义结构体变量
struct student stu;
/*声明完了用结构体类型定义一个结构体变量*/
3、用typedef 方法声明结构体类型后再定义结构体变量
typedef struct student
{
long stuID;
int stuScore;
char stuName[20];
}STUDENT;
STUDENT stu;
/*用typedef写好一个结构体模板,然后写好类型的别名,再定义结构体变量*/
结构体变量的比较
使用==来判定两个结构体是否相等为什么是不合法的?
- 这种操作超出了 C 语言的范围,因为无法实现它始终是和语言的体系相一致的。逐个比较结构成员将是极没有效率的。
- 比较结构中的全部字节是相对较好的方法(许多计算机有特殊的指令可以用来快速执行此类比较)。
- 然而,如果结构含有空洞,那么比较字节会产生不正确的结果。甚至是,假设对应的成员有同样的值,那么出现在空洞中的废弃值也可能会不同。
- 这类问题可以通过下列方法解决,那就是编译器要确保空洞始终包含相同的值(比如零)。
- 然而,初始化空洞会影响全部使用结构的程序的性能,所以它是不可行的。
是否可能会在结构体的开始处有“空洞”?
- 不会的(c标准中空洞只会出现在成员之间和最后)这样的结果是结构第一个成员的地址保证和整个结构的地址完全一样(结构体第一个成员的地址就是整个结构的地址)。(但是,注意两个指针没有相同的类型。)
结构体变量的地址与结构体成员的地址有何不同?
- 结构体变量的地址和结构体成员的地址有何不同?结构体变量的地址是结构体变量所占内存空间的首地址,而结构体成员的地址值与结构体成员在结构体中所处的位置及该成员所占内存的字节数相关。
如果 char 型的值占有一个字节,而 int 型值占用 4 个字节,s 会是多大呢?
- 显然,答案是5 个字节,但可能不是正确的答案。一些计算机要求数据项从某个数量字节(一般是 4个字节)的倍数开始。为了满足计算机的要求,通过在邻近的成员之间留“空洞”(即无用的字节)的方法,编译器会把结构的成员“排列”起来。如果假设数据项必须从 4个字节的倍数开始,那么结构s 的成员 a 将跟着3个字节的空洞。结果是sizeof(s)将为 8。
为什么sizeof 返回的值大于结构大小的期望值,是不是尾部有填充?
- 为了确保分配连续的结构数组时正确对齐,结构可能有这种尾部填充(也可能有内部填充)。即使结构不是数组的成员,尾部填充也会保持,以便 sizeof 能够返回一致的大小。
如何向函数传递结构体?
- 向函数传递结构体变量时,实际传递给函数的是该结构体变量成员值的副本,这就意味着结构体变量的成员值是不能在被调函数中被修改的。
- 和其他变量一样,仅当将结构体的地址传递给函数时,结构体变量的成员值才可以在被调函数中被修改。
向函数传递结构体的完整结构和向函数传递结构体的首地址有何不同?
共用体 (Union )
- 共用体 (Union ),也称 联合 (Union ),是将不同类型的数据组织在一起共同占用同一段内存的一种构造数据类型。
- 共用体虽然也能表示逻辑相关的不同类型的数据集合,但其数据成员是情形互斥的,每一时刻只有一个数据成员起作用。
- 定义一个共用体,包含离婚已婚未婚,同一时刻只有一个是真的。
结构体与共用体(联合)有哪些区别和注意事项?
- 结构体用来表达共存逻辑,而共用体则用来表达互斥逻辑;
- 与结构体变量的初始化不同(结构体变量的成员可以同时初始化,并且可以对具有相同结构体类型的变量进行整体赋值。),共用体变量只能初始化其第一个数据成员;
- 结构体变量可以作为函数参数和函数的返回值,但共用体只能使用指针作为函数参数和返回值,共用体变量不能进行比较操作,也不能作为函数参数;
- 注意共用体成员的访问特点:只有最后一个被赋值的数据成员才有效。这是由共用体在内存中采用覆盖技术,不同成员占用了相同的存储单元决定的。
为什么声明结构体类型时可以包含指向本结构体类型的指针成员?(链表)
- 这是因为指针变量存放的数据是地址,系统为指针变量分配的内存字节数(即存放地址所需的内存字节数)是固定的(对于 32 位计算机系统是 4 个字节),不依赖于它所指向的数据类型。
- 不能包含本结构体类型成员,是因为本结构体类型尚未定义结束,它所占用的内存字节数尚未确定,因此系统将无法为这样的结构体类型分配内存。
动态数据结构和静态数据结构有什么不同?
枚举
- 枚举数据类型 (Enumerated Data Type )描述的是一组整型值的集合,
- 需用关键字 enum来定义。