今日学习任务:
回顾指针;
学习数据结构;
单链表。
今日任务完成情况:
学习链表的基本用法。
今日开发中遇到的问题:
在Linux下有时文件打不开,虚拟机和桌面切换不灵。
今日未解决的问题:
无。
今日开发收获:
复习了学过的知识:结构体,链表
学习了新的知识位域,联合体。
自我评价:
基本满意。
/* 初始化顺序线性表 */
Status InitList(LinkList *L)
{
*L=(LinkList)malloc(sizeof(Node));
if(!(*L))
return ERROR;
(*L)->next=NULL;
return OK;
}
/* 初始条件:顺序线性表L已存在。操作结果:将L重置为空表 */
Status ClearList(LinkList *L)
{
LinkList p,q;
p=(*L)->next;
while(p) {
q=p->next;
free(p);
p=q;
}
(*L)->next=NULL;
return OK;
}
回顾指针:
函数指针,函数指针数组
int (*p)(int,int)
int (*p[])(int,int)
程序示例:函数指针,函数指针数组
1.结构体
2.位域
3.共用体
1.结构体
结构体是自定义数据类型,在结构体中可以包含若干个不同数据类型和不同意义的数据项,组合起来反映某一个信息。
结构体为处理复杂的数据结构(如动态数据结构等)提供了有效的手段,而且,它们为函数间传递不同类型的数据提供了方便。
定义:
struct student{
int id;
char name[20];
int grade[3];
float avg;
};
大括号中的内容称为“成员列表”或“域表”。
每个成员名的命名规则与变量名相同;
数据类型可以是基本变量类型和数组类型,或者是一个结构体类型,即结构体嵌套。
用分号“;”作为结束符。整个结构的定义也用分号作为结束符
结构体类型变量的定义:
struct 结构体名 变量名;
举例:struct student stu;
直接定义结构类型变量 :
struct {
int id;
char name[20];
int grade[3];
float avg;
}stu;
注意区别:stu是结构体变量,没有结构体名,只使用一次,不能再定义新的该类型变量。
提示:可以用typedef 将结构体定义
举例:typedef struct node
{
Datatype data;
struct node * next;
}LinkNode;
举例:LinkNode *Node;
说明:不能将一个结构体类型变量作为一个整体加以引用,只能对结构体类型变量中的各个成员分别引用
结构体变量的成员表示:
结构体变量名.成员名
举例:stu.id\ stu.name \stu.grade stu.avg
如果结构体嵌套,要逐级引用成员变量
struct date{
int year;
int month;
int day;
}
struct student{
int id;
char name[20];
struct date birthday;
int grade[3];
float avg;
}stu;
逐级引用成员变量:stu.birthday.year;
注意结构体变量和数组的区别:
举例:
int a[]={1,2,3}
int b[3];
b=a; 错误
struct student stu1={....},stu2;
stu2=stu1; 正确。
结论:数组是不能彼此赋值的,而相同结构体类型变量可以相互赋值stu2=stu1;
结构体数组
1. 先定义结构体类型,再用它定义结构体数组。struct student stu[10];
2. 定义结构体类型同时定义结构体数组。
struct student{
.....
}
struct student stu[10];
3. 直接定义结构体数组。
struct{
.....
}stu[10];
结构体数组的初始化:
与数组初始化相同! 注意不同元素的成员是不同的类型,注意大括号{}的使用:
struct date{
int year;
int month;
int day;
}
struct student{
int id;
char name[20];
struct date birthday;
int grade[3];
float avg;
}stu;
struct student stu[10]={ {1,"name",{1998,10,1},{80,78,90},85.8} }
struct student stu[10]={ {1,{'n','a','m','e','\0'},{1998,10,1},{80,78,90},85.8} }
用gdb去调试查看具体初始化情况!///
结构体数组的使用:
不能作为整体使用,只能访问数组成员
stu[0].id \stu[0].birthdat.year
结构体指针:
struct 结构体名 *结构指针名;
举例:struct student *pstu;
成员访问:
结构体指针名->成员名
(*pstu).name \ pstu->name
小节
struct student stu[10]; 结构体数组
struct student *stu; 结构体指针
struct student *stu[10]; 结构体指针数组
struct student (*stu)[10]; 指向结构体数组的指针
//结构体变量作为函数参数传递,传值;
结构体数组作为函数参数传递,传址;
//结构体内存对齐问题:
为什么存在内存对齐?
大部分的资料都是这样说的:
1. 平台原因(移植问题):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2. 性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因是,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总结:结构体的内存对齐是拿空间来换时间的做法;
结构体的对齐规则:
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。 注:VS中默认的值为8,Linux中默认值为4
3. 结构体总大小为最大对齐数(每个成员变量除了第一个成员都有一个对齐数)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整数大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
2.位域\位段
把一个字节中的二进制位划分成几个不同的区域,并定义每个区域的位数。
定义:
struct data{
unsigned short a:2;
unsigned short b:3;
unsigned short :3;
unsigned short c:5;
unsigned short d:3;
}
意义:
有些信息的存储不需要一个字节,比如性别,可以用0表示女,1表示男,用一个二进制位就够了。
注意事项:
1)1个位域必须在一个字节内,不能跨两个字节
2)位域占用的位数不能超过一个字节
3)允许无名位域用来占位。
struct data{
unsigned char a:2;
unsigned char b:2;
unsigned char :1;
unsigned char c:3;
}t;
位域使用:
和结构体基本一致
注意:位域的表示范围
t.a=5; 错。因为超出了位域的表示范围
3.共用体:
不同数据类型的数据可以使用共同的存储区域,这种数据构造类型称为共用体
union gy{
int i;
char c;
float f;
}a;
共用体变量在存储时总是按其成员中数据长度最大的成员占用内存空间.
在共用体类型变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员就失去作用.
a.i = 1; a.c = ’a’; a.f = 1.5; 完成以上三个赋值运算以后,a.f是有效的,a.i和a.c已经无意义了。
在程序中经常使用结构体与共用体相互嵌套的形式。 即共用体类型的成员可以是结构体类型,或者结构体类型的 成员是共用体类型。
举例:网络编程
struct sockaddr
{
u_short sa_family;
char sa_data[14];
};
struct sockaddr_in
{
short int sin_family; /* Internet地址族 */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* IP地址 */
unsigned char sin_zero[8]; /* 填0 */
};
struct in_addr
{
unsigned long s_addr;
}
编程中一般并不直接针对sockaddr数据结构操作,而是使用与sockaddr等价的sockaddr_in数据结构