1.结构体概念的引入
c语言提供了众多的数据类型,但是这些数据类型远远达不到我们的需求
比如描述学生,学生信息 : 姓名,性别,年龄。。。。,那么我们通过
什么数据类型描述学生信息呢?答案是自定义数据类型,结构体。
结构体其实是一种复合数据类型
什么是结构体?为什么要用结构体
结构体其实就是自定义数据类型,方便我们更好的管理数据
2.结构体的定义
struct 结构体名
{
成员类型1 成员名1; int a;
成员类型2 成员名2;
成员类型3 成员名3;
};
语法:
结构体名是用来区分不同的结构体
成员,是包含在结构体内部的数据,只在这个结构体有效
可以是任意数据类型
demo:
struct student
{
char name[20]; // 姓名
char sex; // 性别
int age; // 年龄
};
struct student 是我们定义的一个全新的数据类型,名为struct student
这个数据类型包含了姓名,性别,年龄
// 使用方式
int main()
{
struct student st;
}
3.结构体初始化
1.普通初始化
// 给结构体分配栈空间的同时初始化
// 普通初始化,必须将结构体里面的内容全部初始化
struct student st = {"Jack",'m',18};
// 将结构体的里面的内容打印出来
// 如果栈空间分配内存为普通变量,一般访问结构体里面的成员用符号(.)访问
printf("%s,%c,%d\n",st.name,st.sex,st.age);
//scanf("%s,%c,%d\n",st.name,&st.sex,&st.age);
2.指定成员初始化
// 指定成员初始化
struct student st1 = {
.name = "Rose",
//.sex = 'w', // 正确
.age = 18
};
printf("%s,%c,%d\n",st1.name,st1.sex,st1.age);
3.定义结构体的时候初始化
struct student
{
char name[20];
char sex;
int age;
}st2 = {"xiaoming",'M',19};
// 定义结构体的时候就初始化
printf("%s,%c,%d\n",st2.name,st2.sex,st2.age);
4.多结构体使用
// 定义struct date类型
struct date
{
int y; // 年
int m; // 月
int d; // 日
};struct student
{
int age;
// 使用struct date
// 给struct date 分配栈空间
struct date birthday;
//struct student // 重复定义
};
// 普通初始化
int main()
{
// 普通初始化
struct student st = {18,2003,9,19};
printf("%d---%d/%d/%d\n",
st.age,
st.birthday.y,
st.birthday.m,
st.birthday.d
);
return 0;
}
//-------------------------
// 指定成员初始化
int main()
{
// 指定成员初始化
struct student st1 = {
.age = 19,
.birthday.y = 2003,
.birthday.m = 10,
.birthday.d = 11,
};
printf("%d---%d/%d/%d\n", \
st1.age, \
st1.birthday.y, \
st1.birthday.m, \
st1.birthday.d
);
return 0;
}
5.结构体成员引用
结构体成员的引用和普通变量使用没有任何区别
struct student
{
char name[20];
int age;
char sex;
};int main()
{
struct student st;
// 错误,不能直接给数组赋值
//st.name = "jack";
strcpy(st.name,"jack");
char buf[20];
//buf = "Rose";// 错误的
strcpy(buf,"Rose");
st.age = 18;
st.sex = 'm';
printf("%d,%c,%s\n",st.age,st.sex,st.name);
return 0;
}
6.结构体指针,重点
和普通变量的指针没有任何区别
demo:
struct student
{
char name[20];
int age;
char sex;
};int main()
{
// 需要分配堆空间,否则段错误
struct student *st = malloc(sizeof(struct student));
// 注意结构体指针访问成员的时候是用符号(箭头)表示
strcpy(st->name,"jack");
printf("%s\n",st->name);
// 释放堆空间
free(st);
st = NULL;
struct student a;
struct student *q = &a;
(*q).age = 18;// ==> a.age;
int d = 10;
int *p = &d; // *p = 10;d = 10
q->age = 18;
printf("%d\n",q->age); // 指针访问成员用箭头->
printf("%d\n",(*q).age); // 普通变量访问成员用.
return 0;
}
7.结构数组
int a[5]; a[0] = 1; a[1] = 2; a[2] = 3;
struct student class[5];
// 结构体数组普通初始化
struct student
{
char name[20];
int age;
int score;
}st[3] = {
{"jack",18,80},
{"Rose",17,85},
{"xiaoming",19,60}
};
void main()
{
printf("%s,%d,%d\n",st[0].name,st[0].age,st[0].score);
printf("%s,%d,%d\n",st[1].name,st[1].age,st[1].score);
printf("%s,%d,%d\n",st[2].name,st[2].age,st[2].score);
}
// 结构体数组指定成员初始化
struct student1
{
char name[20];
int age;
int score;
};
void main()
{
// 指定成员初始化
struct student class[3] = {
//class[0]
{
.name = "jack",
.age = 18,
.score = 90
},
//class[1]
{
.name = "jack",
.age = 18,
},
//class[2]
{
.name = "jack",
.score = 18,
}
};
printf("%s,%d,%d\n",class[0].name,class[0].age,class[0].score);
printf("%s,%d,%d\n",class[1].name,class[1].age,class[1].score);
printf("%s,%d,%d\n",class[2].name,class[2].age,class[2].score);
}
// 引用结构体数组里面的成员
struct student1
{
char name[20];
int age;
int score;
};
void main()
{
struct student class1[3];
strcpy(class1[0].name,"jack");
class1[0].age = 18;
class1[0].score = 90;
printf("%s,%d,%d\n",class1[0].name,class1[0].age,class1[0].score);
scanf("%s%d%d",(class1+1)->name,&class1[1].age,&class1[1].score);
scanf("%s%d%d",(*(class1+1)).name,&class1[1].age,&class1[1].score);
printf("%s,%d,%d\n",class1[1].name,class1[1].age,class1[1].score);
}
//练习2:
// 定义一个学生信息结构体数组(数组元素的个数由用户决定),依次从键盘输入每个学生
// 信息(姓名,成绩),按成绩的降序输出每个学生的信息,降序算法最好是自己封装函数
// dmeo:
#include <stdio.h>
#include <string.h>
struct student
{
char name[20];
int score;
};
void sort(struct student *st,int len)
{
int tmp;
char tName[20] = {0};
for(int i = 0; i < len-1;i++)
{
for(int j = 0; j < len-i-1; j++)
{
if(st[j].score < st[j+1].score)
{
tmp = st[j].score;
strcpy(tName,st[j].name);
st[j].score = st[j+1].score;
strcpy(st[j].name,st[j+1].name);
st[j+1].score = tmp;
strcpy(st[j+1].name,tName);
}
}
}
}
int main()
{
struct student st[3];
for(int i = 0;i < 3;i++)
scanf("%s%d",st[i].name,&st[i].score);
int len = sizeof(st) / sizeof(struct student);
sort(st,len);
for(int i = 0; i < len; i++)
printf("%s,%d\t",st[i].name,st[i].score);
printf("\n");
return 0;
}
8.结构体封装
结构体一般都是在头文件定义
demo:
#ifndef _MYHEAD_H
#define _MYHEAD_H#include <stdio.h>
#include <stdlib.h>typedef int dataType;
//typedef int * pdataType;//int data;// == dataType data;
//int *p; // == pdataType p;
// 定义结构体类型
typedef struct node
{
int age;
char sex;
dataType data;
}newNode,*pnewNode;// 等价于 typedef struct node newNode; typedef struct node *pnewNode#endif
//------------------------------------
#include "myhead.h"int main()
{
//struct node myNode;
newNode myNode;
myNode.age = 18;
myNode.sex = 'M';
myNode.data = 123;
printf("%d,%c,%d\n",myNode.age,myNode.sex,myNode.data);
//struct node *newNode;
pnewNode newNode = NULL; // 指针变量
newNode = malloc(sizeof(pnewNode));
newNode->age = 18;
newNode->sex = 'W';
newNode->data = 456;
printf("%d,%c,%d\n",newNode->age,newNode->sex,newNode->data);
return 0;
}
总结:
此处typedef如果用在结构体上,可以将这个结构体类型
改为普通变量类型,也可以改为指针变量类型,这样的
好处是第一可以增加代码的可读性,第二让结构体使用起来
比较方便
9.字节对齐(笔试题考的较多)
问题引入?
int a; // 4字节
char b; // 1字节
short c;// 2字节
那么
struct node
{
int a; // 4
char b; // 1
short c; // 2
};
sizeof(struct node)大小??
struct node
{
char a; // 1
char b; // 1
short c; // 2
};
字节对齐概括:
系统为了提高执行效率,计算机从内存中取数据是按照固定的长度取的,比如
32位系统是最大4字节取数据(注意字节对齐方式是可以手动修改),如果是64位系统得看数据的最大长度(long double),如果数据
结构体的存储数据的方式是以成员的最大值为参考值俗称M值
结构体的M值是以对应成员的最大值决定,当然32位系统的M值最大为4
例如:
struct node
{
short a; // 尺寸=2,M值为2
double b; // 尺寸=8;32位系统下M值为4,64位系统M值为8
char c; // 尺寸=1,M值为1
};
struct node n;
32位系统 M值 = max{2,4,1} = 4
4+8+4 = 16 16/m=0
64位系统 M值 = max{2,8,1} = 8
8+8+8 = 24 24/m=0
字节对齐总结:
1.找结构体里面最大的M值
2.在将结构体里面的数据从上往下依次按照自己的M值方式存储,如果存的地址大于M值,就换行
将结构体的空间大小设置成实际成员空间之和
#pragma pack(1) // 将M值设置为1,将此文件下的所有结构体大小设置为实际成员的大小之和
struct node2
{
short a;
char c;
double b;
}__attribute__((packed));
__attribute__((packed))的作用是将结构体大小设置为实际成员大小之和
设置结构体成员的M值、
#include <stdio.h>
#include <stdint.h>
struct node
{
int8_t a __attribute__((aligned(16))); // 其实就是char类型 1字节
int64_t b __attribute__((aligned(8)));// 其实就是 long 类型 8字节
int16_t c __attribute__((aligned(2)));// 其实就是short类型 2字节
};int main()
{
struct node st;
printf("%ld\n",sizeof(int8_t));
printf("%ld\n",sizeof(int64_t));
printf("%ld\n",sizeof(int16_t));
printf("%ld\n",sizeof(st));
return 0;
}