结构体的定义形式

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
}
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐