结构体
1.c语言允许用户自己建立由不同数据组成的组合型的数据结构,它称为结构体。在其他一些高级语言中称为“记录”。
2.定义结构体的一般形式
struct 结构体标记 //struct+结构体标记 即结构体的类型名
{
成员表列
}变量表列;
注意:结构体可以定义在函数外部或者内部。结构体标记和变量至少有一种。如果有变量,可以进行初始化。
3.建立一个结构体类型,它只相当于一个模型,其中没有具体数据,系统也不为其分配存储单元。—―相当于设计好了图纸,但没有建成具体的房屋。
结构体定义和初始化
#include<stdio.h>
//结构体定义总结:结构体标记和变量至少有一种。如果有变量,可以进行初始化。
struct Student{ //1.定义时为结构体起一个结构体标记名
char name[30];
int age;
double score;
};
struct Student2{ //2.定义时为结构体起一个结构体标记名 并定义这种数据类型的变量
char name[30];
int age;
double score;
}A,*p;
struct Student3{ //3.定义时为结构体起一个结构体标记名 并定义这种数据类型的变量 并为变量进行初始化
char name[30];
int age;
double score;
}A1 = {"zhangsan",20},*p1;
struct{ //4.只定义结构体类型的变量
char name[30];
int age;
double score;
}A2,*p3;
void f(void);
int main(void){
//f();
struct Student a = {"zhangsan",20,98.5}; //1.完全初始化
struct Student b = {"zhangsan",20}; //2.不完全初始化,未被赋值的成员将有一个默认值0
struct Student c = a; //3.定义结构体变量时,使用其他的结构体变量完成初始化
//struct Student d = {.score = 18.5}; //4.C99编译器的新功能:在定义一个结构体变量的时候对某个成员初始化,这里会报错
printf("学生的姓名:%s, 年龄:%d, 成绩:%lf\n",a.name,a.age,a.score);
printf("学生的姓名:%s, 年龄:%d, 成绩:%lf\n",b.name,b.age,b.score);
printf("学生的姓名:%s, 年龄:%d, 成绩:%lf\n",c.name,c.age,c.score);
printf("学生的姓名:%s, 年龄:%d, 成绩:%lf\n",d.name,d.age,d.score);
return 0;
}
void f(void){
struct Student a;
printf("请输入学生的姓名,年龄和成绩:\n");
scanf("%s%d%lf",a.name,&a.age,&a.score);
printf("学生的姓名:%s, 年龄:%d, 成绩:%lf\n",a.name,a.age,a.score); //1.可以通过 变量名.成员名 的方式访问一个结构体变量的成员
struct Student *p=&a;
printf("学生的姓名:%s, 年龄:%d, 成绩:%lf\n",p->name,p->age,p->score); //2.可以通过 指针->成员名 的方式访问一个结构体变量的成员
}
结构体变量所占字节长度
#include<stdio.h>
int main(void){
//字节对齐,各种数据类型是按照一定的规则在内存当中排列的
short a;
int b;
double c;
printf("%d,%d,%d\n",(int)&a%2,(int)&b%4,(int)&c%8);//0,0,0 基本数据类型变量的地址是能够被它们所占字节的长度所整除的
struct Stu1{
char a;
short b;
};
struct Stu2{
char a;
int b;
};
struct Stu1 A;
struct Stu2 B;
printf("%d,%d\n",(int)&A%2,(int)&B%4); //0,0 1.结构体变量的首地址能够被其最宽基本类型成员的大小所整除
printf("%d,%d\n",sizeof(A),sizeof(B)); //4,8 2.结构体成员相对于结构体变量的首地址的偏移量(结构体变量的地址编号和结构体成员的地址编号所差的字节数)都是其成员大小的整数倍
printf("%d,%d\n",((int)&A.b-(int)&A)%2,((int)&B.b-(int)&B)%4); //0,0
struct Stu3{
char a;
double f;
int b;
}C;
printf("%d\n",sizeof(C)); //24 3.结构体的总大小为结构体最宽基本类型成员的整数倍
struct Stu4{
char a;
struct Stu2 b;
}D;
/*
D中最宽的基本类型成员是D.b.b 长度为4个字节,所以
1.D的首地址必须被4整除
2.D的长度必须被4整除
struct Stu2数据类型可知:
1.D.b的地址能够被4整除
2.D.b的长度是8
*/
printf("%d\n",sizeof(D)); //12 2【特殊】.如果结构体成员是复合数据类型的,那么它相对于结构体首地址的偏移量,应该是其最宽基本类型成员大小的整数倍
return 0;
}
结构体数组和指针
输入n个学生的姓名和成绩并按成绩排序输出
#include<stdio.h>
#define N 4
struct Stu{
char name[30];
double score;
};
void input(struct Stu *p,int len){
for(int i=0;i<len;++i){
printf("请输入第%d个学生的姓名和成绩:\n",i+1);
scanf("%s%lf",p[i].name,&p[i].score);
}
}
void show(struct Stu *p,int len){
for(int i=0;i<len;++i){
printf("第%d个学生的姓名:%s,成绩:%lf\n",i+1,p[i].name,p[i].score);
}
}
void sort(struct Stu *p,int len){
for(int i=0;i<len-1;++i){
for(int j=0;j<len-1-i;++j){
if(p[j].score>p[j+1].score){
struct Stu b=p[j];
p[j]=p[j+1];
p[j+1]=b;
}
}
}
}
int main(void){
//struct Stu m[]={{"zhangsan",89.6},{"lisi",67},{"wangwu",23.5}}; //结构体数组定义时的初始化
//show(m,3);
struct Stu a[N];
input(a,N);
sort(a,N);
show(a,N);
return 0;
}
复杂的数据类型
1.已知 int a[5]是定义一个含有5个整型元素的数组,那么如何理解 int(* p)[5]所代表的含义?我们把 int[5] 当成是一个数据类型,并设一个数据类型 A 和其等价,则 int a[5] 和 A a 等价。同理我们可以把 int(* p)[5]转化成 A* p。
因为 A 是一个含有5个整型元素数组类型,所以 A* 就代表是一个含有5个整型元素的数组的指针类型,那么 p 就是该类型的变量。
2.已知 int f(int) 是定义一个返回值是 int,形参为 int 的函数。那么如何理解 int (* p)(int) 所代表的含义?
我们把 int (int) 当成是一个数据类型,并设一个数据类型 A 和其等价,则 int f(int) 和 A f 等价。
同理我们可以把 int(* p)(int) 转化成 A* p。
因为 A 是一个返回值是 int,形参为 int 的函数类型,所以 A* 就是一个返回值是 int,形参为 int 的函数的指针类型,那么 p 就是该类型的变量。
结论:看变量所属圆括号的最外层,把最外层的数据类型设为一个新数据类型,然后去掉变量最外层的花括号。通过这种方法,直到原式转化成 “数据类型 变量” 的形式。
3.分析:int a[3][4] 中 a 的数据类型是什么?
(1)设数据类型 A 和 int[4] 等价,都是定义一个含有4个 int 的数组。则原式可以转化为:A a[3]。
(2)设数据类型 B 和 A[3] 等价,都是定义一个含有3个 A 的数组。则原式可以转化为 B a。 所以 a 是一个含有3个 A 的数组。
(3)又因为 A 是一个含有4个 int 的数组,所以 p 就是一个含有3个元素的数组,每个元素又是一个含有4个 int 的数组。
4.分析:int* (* (* p ) [10] )(void) 中 p 的数据类型是什么?
(1)设数据类型 A 和 int * (void) 等价,都是定义一个返回值为 int* ,形参为 void 型的函数。
则原式可以转换为A* (* p) [10]。
(2)设数据类型 B 和 A* [10] 等价,都是定义含有10个 A 的指针的数组。
则原式可以转换为 B* p。那么 p 就是一个含有10个 A 的指针的数组的指针。
(3)又因为 A 的数据类型是一个返回值为 int*,形参为 void 型的函数。 所以 p 就是一个含有10个(返回值为 int*,形参为 void 型的函数)的指针的数组的指针。
你也可以说 p 指向一个含有10个元素的数组,其中每个元素是一个返回值为 int* ,形参为 void 型的函数的指针。
用 typedef 声明新类型名
按照定义变量的方式,把变量名换上新类型名,并且在最前面加“typedef”,就声明了新类型名代表原来的类型。
#include<stdio.h>
typedef int Count; //1.Count 就代表 int 类型
typedef int Num[3]; //2.Num 代表一个含有3个 int 型元素的数组类型
typedef struct{
int a;
double b;
}Stu; //3.Stu代表一个结构体类型
#define INT int //4.这里是定义了一个宏名INT,只是在预编译阶段进行一些简单的字符串替换
int main(void){
Count a=3; //1.定义一个 int 型变量 a
printf("%d\n",a);//3
Num b={1,2,3}; //2.定义了一个含有3个 int 型元素的数组 b
printf("%d,%d,%d\n",b[0],b[1],b[2]);//1,2,3
Stu m={12,45.6}; //3.定义了一个结构体类型变量 m
printf("%d,%lf\n",m.a,m.b);//12,45.600000
INT c=10; //4.在预编译阶段 该句被替换为 int c=10;
printf("%d\n",c);//10
return 0;
}
单向动态链表
一般头结点不存放有效数据(当然你也可以根据情况而定),在其他节点存放有效数据。
共用体
(暂无整理)
枚举类型
(暂无整理)