数据结构-结构体
结构体的定义形式1、概念结构体是一种构造类型的数据结构,是一种或多中基本类型或构造类型的数据的集合。2、结构体的定义//先定义类型 在定义变量(推荐)stuct stu{int num;char name[32];int age;};//定义结构体类型struct stu xxx;//定义结构体变量//定义类型的同时 定义变量stuct stu{int num;char name[32];int
结构体的定义形式
1、概念
结构体是一种构造类型的数据结构,
是一种或多中基本类型或构造类型的数据的集合。
2、结构体的定义
//先定义类型 在定义变量(推荐)
stuct stu
{
int num;
char name[32];
int age;
};//定义结构体类型
struct stu xxx;//定义结构体变量
//定义类型的同时 定义变量
stuct stu
{
int num;
char name[32];
int age;
}xxx;
stuct stu xxxx;//可以使用此类型在此定义变量
//定义一次性结构体
struct
{
int num;
char name[32];
int age;
}tom;
//无法使用此结构体类型在此定义变量;
struct 是结构体的关键字stu是结构体的类型名
使用结构体类型 必须是struct stu
num 、name 、age叫做结构体的成员
定义类型的时候,不需要给成员符初值
struct stu
{
int num;//4B
char name[32];//32B
int age;//4B
};
void test01()
{
printf("%d\n",sizeof(struct stu));//40B
}
void test02()
{
struct stu lucy;//lucy是结构体变量
//通过结构体变量 访问结构体成员(一定要遵循成员自身的类型)
printf("num = %d\n",lucy.num);
printf("name = %s\n",lucy.name);
printf("age = %d\n",lucy.age);
//结构体成员赋值 (一定要遵循成员自身的类型)
lucy.num = 100;
strcpy(lucy.name,"lucy");
lucy.age = 18;
printf("num=%d,name=%d,age=%d\n",lucy.num.lucy.name,lucy.age);
}
3、结构体变量初始化
void test03()
{
//初始化的顺序 必须和成员的顺序一致
struct stu lucy = {100,"lucy",18};
printf("num=%d,name=%d,age=%d\n",lucy.num.lucy.name,lucy.age);
//清空结构体变量
struct stu bob;
memset(&bob,0,sizeof(bob));
}
4、结构体变量赋值
void test04()
{
struct stu lucy = {100,"lucy",18};
struct stu bob;
//方法1逐个成员赋值
/*
bob.num = lucy.num;
strcpy(bob.name,lucy.name);
bob.age = lucy.age;
*/
//方法2相同类型的结构体变量可以直接赋值
/* bob = lucy; */
//方式3整个地址内容拷贝
memcpy(&bob,&lucy,sizeof(struct stu));//常用方法之一,通常与清空结构体内容一起用。
printf("num=%d,name=%d,age=%d\n",lucy.num.lucy.name,lucy.age);
}
5、结构体数组
使用结构体类型修饰数组,本质是数组,数组的每个元素是结构体。在复杂的项目组十分常见。
void test05()
{
struct stu arr[5] = {
{100,"aa",19},
{99,"bb",20},
{98,"cc",21},
{97,"dd",22},
{96,"ee",23},
};
int n = sizeof(arr)/sizeof(arr[0]);
int i = 0;
for( i = 0;i < n;i++)
{
printf("num=%d,name=%d,age=%d\n",arr[i].arr[i].name,arr[i].age);
}
}
6、typedef修饰词
typedef给已有的类型七个别名,但是不能创造新的类型,常用来修饰结构体类型。
struct stua
{
int num;
char name[32];
float score;
};
struct stua xxx;//定义结构体变量
typedef struct stub
{
int num;
char name[32];
float score;
}STUB;//STUB 就是struct stub的结构体类型
//typedef struct stub STUB;//上述写法可以简写为此写法
STUB xxx;//定义结构体变量
7、结构体指针
本质是指针,指向的结构体地址,常用与函数的传参等场景。
typedef struct stu
{
int num;
char name[32];
float score;
}STU,*STU_P;
//STU就是struct stu的类型
//STU_P就是struct stu *的类型
void test06()
{
STU lucy={100, "lucy", 99.9f};
//STU_P p=&lucy;
STU *p = &lucy;
printf("num = %d,name=%s,score=%f\n", lucy.num, lucy.name,lucy.score );
printf("num = %d,name=%s,score=%f\n", (*p).num, (*p).name,(*p).score );
//直接通过p访问lucy成员
printf("num = %d,name=%s,score=%f\n", p->num, p->name, p->score );
//结构体变量就用 " . "访问结构体常用,结构体指针变量使用“ ->”访问结构体成员
printf("num = %d,name=%s,score=%f\n", (&lucy)->num, (&lucy)->name, (&lucy)->score );
}
8、结构体指针作为函数的参数
把结构体的地址传递然后修改地址对应空间的内容
//使用上段定义的结构体类型
void funca(STU *p)
{
printf("输入成员值num name score:");
scanf("%d %s %f",&p->num,p->name,&p->score);
}
void funcb(STU *p)
{
printf("num = %d,name=%s,score=%f\n", p->num, p->name, p->score );
}
void test07()
{
STU lucy;
funca(&lucy);
funcb(&lucy);
}
9、指针作为结构体的成员
结构体变量在栈区,结构体中指针成员指向堆区
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
int num;
char *name;
float score;
}STU;
void test08()
{
STU lucy;//lucy在栈区
lucy.num = 100;
lucy.score = 99.9f;
//结构体中指针成员申请堆区空间
lucy.name = (char *)calloc(1,strlen("lucy")+1);
strcpy(lucy.name, "lucy");
printf("num=%d,name=%s,score=%f\n",\
lucy.num, lucy.name,lucy.score);
if(lucy.name != NULL)
{
free(lucy.name);
lucy.name = NULL;
}
}
int main(int argc, char const *argv[])
{
test08();
return 0;
}
指针作为结构体的成员存在风险(浅拷贝风险)
结构体整体赋值时,结构体内有指针成员的情况下,会出现两个指针成员指向同一块地址的情况,当释放其中一个指针变量时,另外一个成员数据也将会消失。
typedef struct
{
int num;
char *name;
float score;
}STU;
void test09()
{
STU lucy;//lucy在栈区
lucy.num = 100;
lucy.score = 99.9f;
lucy.name = (char *)calloc(1,strlen("lucy")+1);
strcpy(lucy.name, "lucy");
printf("num=%d,name=%s,score=%f\n",\
lucy.num, lucy.name,lucy.score);
STU bob;
bob = lucy;//会出现浅拷贝(此种方式只能用于结构体中 没有指针成员)
if(lucy.name != NULL)
{
free(lucy.name);
lucy.name = NULL;
}
if(bob.name != NULL)
{
free(bob.name);
bob.name = NULL;
}
}
结构体在堆区,结构体的指针成员指向堆区
void test10()
{
STU *p = (STU *)calloc(1,sizeof(STU));
p->num = 100;
p->score = 99.9f;
p->name = (char *)calloc(1,strlen("lucy")+1);
strcpy(p->name, "lucy");
printf("num=%d,name=%s,score=%f\n",\
p->num, p->name,p->score);
//注意空间释放顺序
if(p->name != NULL)
{
free(p->name);
p->name = NULL;
}
if(p != NULL)
{
free(p);
p=NULL;
}
}
10、结构体内存分配
结构体对齐
确定结构体的大小
1、先确定分配单元,有结构体成员中最大的基本类型决定
2、确定每个成员的其实偏移量,成员的基本类型的整数倍
3、结构体的总大小,分配单元的整数倍。
#include <stdio.h>
typedef struct
{
char a;//1B
int b;//4B
}DATA1;
void test11()
{
printf("%d\n",sizeof(DATA));//8B
DATA1 data;
printf("%u\n",&data.a);
printf("%u\n",&data.b);//打印地址空间编号
}
typedef struct
{
int a;
char b;
short c;
char d;
}DATA2;
void test12()
{
printf("%d\n",sizeof(DATA2));//12B
DATA2 data;
printf("%u\n",&data.a);
printf("%u\n",&data.b);//打印地址空间编号
printf("%u\n",&data.c);
printf("%u\n",&data.d);//打印地址空间编号
}
下图为DTAT2空间分布
11、共用体
共用体的成员 共享同一块空间
空间的大小 由最大的类型决定
union data
{
char a;
short b;
int c
};//a、b、c共用一块空间
union DATA
{
char a;
short b;
int c
};//a、b、c共用一块空间
void test13()
{
printf("%d\n",sizeof(union DATA));
union DATA data;
data.a = 10;
data.b = 20;
data.c = 30;
printf("a = %d,b = %d,c = %d\n",data.a,data.b,data.c);//30\30\30
printf("%d\n",data.a+data.b+data.c);//90
}
共用体的成员虽然共享一块空间,但是每个成员能操作的空间大小 是由成员自身的类型大小决定
12、结构体嵌套结构体
typedef struct
{
int x;
int y;
}DATA3;
typedef struct
{
int a;
int b;
DATA3 c;//结构体嵌套结构体
}DATA4;
void test14()
{
//结构体嵌套结构体:访问到最底层
DATA4 data;
data.a = 10;
data.b = 20;
data.c.x = 30;
data.c.y = 40;
printf("%d\n", data.c.y);
}
结构体嵌套结构体的对齐问题
1、确定分配单元,所有结构体中最大的基本类型决定
2、确定每个成员起始位置的偏移量 = 成员的基本类型的整数倍
3、结构体的总大小==分配单位的整数倍,总大小等于最大基本类型的整数倍
typedef struct
{
short d;
char e;
}DATA1;
typedef struct
{
short a;
int b;
DATA1 c;//结构体成员为结构体
short f
}DATAA;
成员位置不同,结构体的大小可能也不一样
13、强制类型对齐
指定对齐原则:
使用#pragma pack 改变默认对齐原则
格式:
#pragma pack (value)指按value值进行对齐
注意:
1、value只能是:1 2 4 8等值
#include <stdio.h>
//指定对齐规则
#pragma pack(2)
typedef struct
{
char a;
int b;
short c;
}DATA1;
#pragma pack(4)
typedef struct
{
char a;
int b;
short c;
}DATA2;
void test15()
{
printf("%d\n",sizeof(DATA1));//8
printf("%d\n",sizeof(DATA2));//12
}
14、位段
在网络编程中经常使用,手动填充DNS或IP报文头部数据时 需要逐位填充相应数据。
typedef struct
{
unsigned char a:2;
unsigned char b:2;
//位段的压缩 不能操作自身成员的类型大小
unsigned char c:5;
}DATA2;
typedef struct
{
unsigned char a:2;
unsigned char :2;//占用两位空间,下面数据需右移两位
unsigned char b:2;
}DATA3;
typedef struct
{
unsigned char a:2;
unsigned char :0;//跳过此基本类型,另起一个位置
unsigned char b:2;
}DATA4;
void test16()
{
printf("%d\n",sizeof(DATA2));//2
DATA2 data;
data.a = 6;//0110 只能去低两位二进制数
printf("data.a = %d\n",data.a);//2
printf("DATA3 = %d\n",sizeof(DATA3));//1
printf("DATA4 = %d\n",sizeof(DATA4));//2
}
更多推荐
所有评论(0)