c-结构体笔记

1.结构体初识

1.结构体是什么东西?
1.1 是一种新的数据类型---->自定义类型
1.2 为什么有结构体
1.3 完整定义结构体的语法
struct 标识符
{
基本数据类型;
};

常见的定义方式

struct MM			//struct MM : 类型
{
	char name[20];
	int age;
	int num;
};
struct MM object;
//object:  结构体变量    struct MM: 自定义的数据类型

不常见的定义方式

struct 			
{
	char name[20];
	int age;
	int num;
} role;            //role:唯一一个结构体变量,且可以初始化

struct 			
{
	char name[20];
	int age;
	int num;
} role={“g_role”,29,1004};   //初始化,缺点:指针访问不了结构体变量
int main()
{
    role.age = 38;
	role.num = 9999;
	return 0;  //因为没有数据类型名,所以定义不了其他变量,故是只有一个变量的结构体
}
  1. 如何定义结构体变量
    int num;
    struct 结构体名 变量名;
    int p;
    struct 结构体名
    指针名;
    int array[4];
    struct 结构体名 数组名[数组长度]
//1.结构体变量
	struct MM myMM;
//2.结构体指针
	struct MM* pMM;
//3.结构体数组
	struct MM array[4];

3.如何访问结构体中成员
3.1 变量去访问: 变量.成员
3.2 指针访问: 结构体指针->成员
(*结构体指针).成员

    struct MM myMM;
	printf("请输入姓名,年龄,编号:");
	scanf("%s%d%d", myMM.name, &myMM.age, &myMM.num);//输入数据
	printf("%s\t%d\t%d\n", myMM.name, myMM.age, myMM.num);

定义变量初始化

struct MM g_MM = {"Baby",18,1002};  //全局变量
int main()
{
    //1.1 定义变量去初始化
    //定义变量时可以初始化,初始化的顺序一定要和结构体定义变量的顺序一致
	struct MM yourMM = {"Baby",18,1002};  //
	//错误写法,分开写错误
	//struct MM object;
	//object = { "Baby",18,1002 };
	return 0;
}

2.结构体数组

结构体数组:
struct 结构体名 数组名[数组长度]

struct MM 
{
	char name[20];
	int age;
	int num;
};
int main() 
{
	struct MM myMM[3]; //myMM[0]  myMM[1] myMM[2]
	printf("请输入三个学生的信息:\n");
	for (int i = 0; i < 3; i++) 
	{
		printf("请输入第 %d 位的信息:", i + 1);
		//myMM[0]
		//myMM[1]
		scanf("%s%d%d", myMM[i].name, &myMM[i].age, &myMM[i].num);
		//scanf("%s%d%d", (myMM+i)->name, & (myMM+i)->age, & (myMM+i)->num);
	}
	printf("姓名\t年龄\t编号\n");
	for (int i = 0; i < 3; i++) 
	{
		printf("%s\t%d\t%d\n", myMM[i].name, myMM[i].age, myMM[i].num);
	}
	return 0;
}

3.结构体指针

    //错误
	//int* p = NULL; //(void *)0  强制转换语法 (目标类型)变量
	//*p = 100;

指针要充当变量使用: 变量存值

	//1. 指向变量
	struct MM myMM;
	p = &myMM;
	strcpy(p->name, "ILoveyou");
	p->age = 18;
	p->num = 1001;
	printf("%s\t%d\t%d\n", myMM.name, myMM.age, myMM.num);
	printf("%s\t%d\t%d\n", (*p).name, (*p).age, (*p).num);
	printf("%s\t%d\t%d\n", p->name, p->age, p->num);
//2. 动态内存申请
	struct MM* pMM = (struct MM*)malloc(sizeof(struct MM));
	strcpy(pMM->name, "MM");
	pMM->age = 18;
	pMM->num = 1001;
	//动态内存申请更多的是为结构体数组存在
struct MM* pArray = (struct MM*)malloc(sizeof(struct MM) * 3);
//更多的是用这种方法
//动态内存申请是为了结构体数组存在
//结构体指针变成结构体数组

4.typedef和结构体的定义

typedef用于起别名

struct MM 
{
	char name[20];
	int age;
	int num;
};
typedef struct MM MMTYPE;
typedef struct MM* LPMM;
//定义方式与以下方式相同
typedef int INT
typedef int * LPINT;
typedef int array[2];

使用

struct student_afihaoicnab
{
	char name[20];
	int age;
};
typedef struct student_afihaoicnab stu;
//也可以写成
//typedef struct student_afihaoicnab
//{
//	char name[20];
//	int age;
//}stu;
stu* Lpstu2 = (stu*)malloc(sizeof(stu));
Lpstu2->age = 18;
strcpy(Lpstu2->name, "li");
printf("%s\t%d\t\n", Lpstu2->name, Lpstu2->age);
#include <stdio.h>
#include <string.h>
#include <windows.h>
typedef struct student_score_info 
{
	char name[20];
	int age;
	int num;
}SSI,*LPSSI,SARRY[2];
/*

typedef struct student_score_info  SSI;
typedef int INT;

typedef struct student_score_info* LPSSI
typedef int * LPINT;

typedef struct student_score_info SARRY[2]
typedef int array[2];

*/

struct MM 
{
	char name[20];
	int age;
	int num;
};
typedef struct MM MMTYPE;
typedef struct MM* LPMM;
int main() 
{
	//正常写法
	struct student_score_info mm;
	SSI  t_mm;
	struct student_score_info* pMM = &mm;
	LPSSI p = &mm;  //结构体指针的别名一定要有标识前缀,如LPSSI的LP
	//LPVOID lpvoid = NULL;  //void *
	struct student_score_info array[2];
	SARRY myarray;
	return 0;
}

5.结构中的指针

struct boy
{
	char *name;         //指针的时候不能直接赋值,用字符串拷贝函数
	int age;
	int num;
};
int main()
{
	struct boy myboy;
	char InputName[20];
	printf("请输入姓名:");
	scanf("%s", InputName);
	//赋值的方法
	myboy.name = (char *)malloc(strlen(InputName) + 1);
	strcpy(myboy.name,InputName);          //复制
	myboy.age = 18;
	myboy.num = 1001;
	printf("%s\t%d\t%d\t\n", myboy.name, myboy.age, myboy.num);
	free(myboy.name);
	myboy.name = NULL;
	return 0;
}

6.结构体的内存问题

结构体中不允许空的结构体

struct MM //不允许空的结构体
{
	
};
struct MM 
{
	char name[3];	//3
	int inum;		//4
	int age;		//4
	double num;		//8
};
内存问题: 
	//占用多少内存只和内存申请有关系
	struct MM* pMM = (struct MM*)malloc(sizeof(struct MM));
不对齐 所有内存之和
	             对齐的方式: 地址总线
节省内存的写法:
	              字符当一个字节处理
	              从小到大写
struct MM 
{
	char name[3];	//3  小的  不论name[i]中i有多少 只按数据类型(char)占用的字节比较
	int inum;		//4
	int age;		//4
	double num;		//8  大的
};
具体去算字节数:
struct MM 
{
	char name[3];	//占用3个字节
	int inum;		//4
	double num;		//8
};
int main() 
{
	//占用多少内存只和内存申请有关系
	struct MM* pMM = (struct MM*)malloc(sizeof(struct MM));
	printf("%d\n", sizeof(struct MM));  //输出占用16个字节
	return 0;
}

在这里插入图片描述
共占用16的字节
如果这样写

struct MM
{
	char name[3];	//占用3个字节
	double num;		//8
	int inum;		//4
};

char需要补5个字节,int需要补4个字节 ,一个结构体共24个字节

7.联合体和枚举类型(比较鸡肋)

7.1联合体

1.联合体相当于共用体
     	所有变量的共用最长的内存.,所有变量用同一块内存
    	struct 关键字改为union,用法相同
union MM    //变量公用
{
	char name[30];  //这三个变量用的是char name[30];的内存
	int age;
	int num;
};     

一次只能初始化一个变量

union MM myMM = { "Iloveyou"};         //正确
union MM myMM = { "Iloveyou"18,1001};//报错

仍可以拷贝

union MM
{
	char name[30];
	int age;
	int num;
};
int main() 
{
	union MM myMM = { "10"};
	strcpy(myMM.name, "1001");
	printf("%s\n", &myMM.age);  //打印1001
	//因为三个变量公用char内存,对char name初始化,age,num也用name
	myMM.age = 99;
	printf("%d\n", myMM.age);  //打印99
	printf("%s\n", &myMM.name);//99在阿斯克码表中为c
	printf("%d\n", myMM.num);//99
}

7.2枚举类型

2.枚举类型: 整形常量
    	项目一些具有代表性的常量的符号替换
//项目一些具有代表性的常量的符号替换
enum direction {UP=72,DOWN=80,LEFT=75,RIGHT=77};
//默认的值:0 1 2....
//enum weekdays{一,二,三,四,五,六,七};//默认初始化0~6
enum weekdays{,,,,,,};
//局部的默认: 是前者的+1
enum COLOR {RED=1,BLACK,YELLOW=9,BLUE};//BLACK默认2,BLUE默认10
int main()
{
    char userKey = _getch();
	switch (userKey) 
	{
	case UP:
		printf("%d\t",);
		break;
	case DOWN:
		printf("%d\t",);
		break;
	case LEFT:
		printf("%d\t",);
		break;
	case RIGHT:
		printf("%d\t",);
		break;
	}
	return 0;
}
printf("BLACK:%d\t,BLUE=%d\n", BLACK, BLUE);

8.位段结构体(多用于电路程序设计)

精准到位

struct flag 
{
    //:后面表示用几位二进制位存储
	unsigned int a : 2;  //32λ    11   //3  表示用2位二进制存储
	unsigned int b : 3;	 //111			//7
	unsigned int c : 4;	 //1111			//15
	unsigned int d : 4;	 //1111			//15  表示用4位二进制存储
};
int main() 
{
	struct flag myFlag = { 3,8,17,15 };
	printf("%d\t%d\t%d\t%d\n", myFlag.a, myFlag.b, myFlag.c, myFlag.d);
	//                              3        0        1           15
	return 0;
}

9.结构体嵌套

    1.以另一个结构体变量为数据成员
    2.以另一个结构体指针为数据成员

不常见的写法
1

struct A //A结构体中直接嵌套一个B结构体
{
	int num;
	struct B
	{
		int age;
	};
};
int main()
{
    struct A aObject = { 11,23 };//可以不使用第二个结构体变量就可以直接赋值
	printf("%d\t%d\n", aObject.age, aObject.num);
	struct A* pA = &aObject;//也可以由指针访问
	printf("%d\t%d\n", pA->age, pA->num);
    return 0;
}

2

struct  MM 
{
	int num;
	struct GG 
	{
		int age;
	}boy;
};
int main()
{
    struct MM mmObject = { 121,134 };
	printf("%d\t%d\n", mmObject.num, mmObject.boy.age);//前缀mmObject.boy.age剥洋葱法
return 0;}

比较常见的写法,以结构体变量为成员的写法
1

struct score 
{
	int math;
	int english;
};
struct student 
{
	struct score stuScore;
	char name[20];
	int num;
};
    struct student Bug = { 23,34,"张三",1001 };//初始化的两种方法
	struct student Liu = { {23,434},"Liu",1002 };
	//剥洋葱
	printf("%d\t%d\t%s\t%d\n", Liu.stuScore.math,Liu.stuScore.english, Liu.name, Liu.num);

2指针方式嵌套

struct score 
{
	int math;
	int english;
};
struct studentInfo 
{
	struct score* pScore;  //*pScore是一个指针,没有内存,不能充当变量,
	                       //需要先申请内存,然后数据初始化
	char name[20];
	int num;
};
int main()
{   //申请内存
    struct studentInfo* p = (struct studentInfo*)malloc(sizeof(struct studentInfo));
    //上方的指针申请与pScore无关,需要下方对pScore申请内存
	p->pScore = (struct score*)malloc(sizeof(struct score));
	p->pScore->english = 14;
	p->pScore->math = 100;
	strcpy(p->name, "迪丽热巴");
	p->num = 1004;
	printf("%d\t%d\t%s\t%d\n", p->pScore->math, p->pScore->english,
		p->name, p->num);
	system("pause");
    return 0;
}

上方一定需要考虑指针是否能充当变量使用

10.结构体数组对于数据的一些基本操作

表格数据的增删改查
    1.传参写法: 局部变量    尽量使用传参写法
     2.不传参写法: 全局变量
     数组写法的局限性,就是大小问题

11.结构体变量与函数的关系

struct MM 
{
	char name[20];
	int age;
	int num;
};
void printMM(struct MM object) 
{
	printf("%s\t%d\t%d\n", object.name, object.age, object.num);
}
void modify(struct MM object)
{
	struct MM temp = { "迪丽热巴",18,1003 };
	object = temp;
	printMM(object);
}
int main()
{
	struct MM mm = { "刘亦菲",28,1001 };
	modify(mm);
	printMM(mm);
	return 0;
}

输出时函数改了,main里面没有改
在这里插入图片描述
解决办法
要修改实参的值就要传实参的地址

struct MM 
{
	char name[20];
	int age;
	int num;
};
void printMM(struct MM object) 
{
	printf("%s\t%d\t%d\n", object.name, object.age, object.num);
}
void modify(struct MM *p)
{
	struct MM temp = { "迪丽热巴",18,1003 };
	*p = temp;
	printMM(*p);
}
int main()
{
	struct MM mm = { "刘亦菲",28,1001 };
	modify(&mm);
	printMM(mm);
	return 0;
}

在这里插入图片描述
结构体返回指针问题

//结构体返回指针的问题
struct MM* createMM()  //讲指针变成变量
{
	struct MM* pmm = (struct MM*)malloc(sizeof(struct MM)); //把指针变成变量.
	return pmm;  //指针变量
}
int main()
{
    //若在主函数中直接定义指针,不是变量
    //struct MM* pMM;
    //pMM->name;    不是变量,所以不能直接赋值
    //pMM->age;
    struct MM* pMM=cerateMM();//此时pMM是一个变量,可以直接赋值
    strcpy(p->name,"afafaagwtbw");
    pMM->age-18;
    pMM->num=1234;
    return 0;
}

只要指针能够指向一个结构体变量,申请了内存,它就能充当一个结构体变量使用。
指针变成变量的方法
1.指针指向向变量:用变量地址赋值。
2.动态内存申请。
动态内存申请

struct MM* initMM(const char* name, int age, int num) 
{
	struct MM* pmm = (struct MM*)malloc(sizeof(struct MM)); //把指针变成变量.
	strcpy(pmm->name, name);
	pmm->age = age;
	pmm->num = num;
	return pmm;
}

12.什么是链表

链表就是一个结构体变量

struct people
{
	char name[20];
	int age;
	int num;
	char addr[20];
};

struct Node
{
	//int data;       //数据域
	struct MM data;   //指针域
	struct Node* next;
};

链表的一个结点包括数据域和指针域
在这里插入图片描述
用第一个节点表示整个链表
头节点:没有存数据 有头链表,常用
首节点:存数据 无头链表

12.1静态内存申请

手动申请,手动释放

#include <stdio.h>
struct MM 
{
	char name[20];
	int age;
	int num;
};

struct Node 
{
	//struct MM data;
	int data;			//数据域
	struct Node* next;	//指针域
};

int main()
{
	struct Node node1 = { 1,NULL };
	struct Node node2 = { 2,NULL };
	struct Node node3 = { 3,NULL };
	node1.next = &node2;
	node2.next = &node3;
	struct Node* pMove = &node1;
	while(pMove!=NULL)
	{
		printf("%d\t", pMove->data);
		pMove = pMove->next;
	}
	//1.链表是什么?
	//2.链表怎么表示
	//3.链表如何打印

	return 0;
}

12.2动态内存申请

#include <stdio.h>
#include <stdlib.h>
struct Node //节点结构体
{
	int data;
	struct Node* next;
};
1.没有在封装
用第一个节点表示整个链表
有头链表: 1.创建表头--->创建结构体变量
用指针表示结构体变量?
   1.1用变量地址初始化,
   1.2动态内存申请
//1.创建表头,即创建结构体变量
struct Node* createHead()
{
	//1.动态内存申请
	struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
	//(*headNode).next = NULL;
	headNode->next = NULL;//变量要初始化
	return headNode;   //返回变量
}

在这里插入图片描述
2.创建节点—>把用户数据加工成节点为插入准备

struct Node* createNode(int data) 
{   
	struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
	newNode->data = data;
	newNode->next = NULL;  //作为链表结束标志
	return newNode;
}

在这里插入图片描述

//3.表头插入
//自己写时候,参数要能够解释出意思
//以headNode 为表头链表
//插入的数据是data
void insertByHead(struct Node* headNode,int data)
{
	struct Node* newNode = createNode(data);	//调用自己写的函数
	//下面两句先后顺序不能交换
	//先连后断
	newNode->next = headNode->next;
	headNode->next = newNode;
	/*
		headNode->next = newNode;
		newNode->next = headNode->next;  //newNode->next = newNode
	*/
}

在这里插入图片描述

//4.表尾插入
void insertByTail(struct Node* headNode, int data) 
{
	struct Node* newNode = createNode(data);
	struct Node* pTail = headNode;
	while (pTail->next != NULL) 
	{
		pTail = pTail->next;
	}
	pTail->next = newNode;
}
//5.指定位置插入
//data:插入的数据
//posData: 指定位置
void insertByAppoin(struct Node* headNode, int data, int posData) 
{
	//做这种查找类插入,先不要着急创建节点?考虑没找到情况
	struct Node* frontNode = headNode;		//左
	struct Node* posNode = headNode->next;	//右
	//注意: && 短路处理
	//posNode != NULL作为找到表尾的退出条件
	//当posNode等于NULL ,NULL->data 的非法访问
	//frontNode->next!=NULL&&posNode->data != posData
	while (posNode != NULL && posNode->data != posData )//难点
	{
		//frontNode = frontNode->next;
		//posNode = posNode->next;
		frontNode = posNode;
		posNode = frontNode->next;  //posNode=posNode->next;
		//frontNode 是posNode左边
		//最后一节点:frontNode  posNode就是NULL
	}
	//退出循环,分析讨论是否找到
	if(posNode==NULL)
	{
		printf("%d\t", frontNode->data);
		printf("未找到,无法插入信息!\n");
	}
	else
	{
		struct Node* newNode = createNode(data);
		frontNode->next = newNode;
		newNode->next = posNode;
	}
}

在这里插入图片描述
删除操作:
打狗要看主人: 把一个节点下一个删除,要考虑删除节点上一节点next
不能删除NULL的东西

//6.表头删除
void deleteByHead(struct Node* headNode) 
{
	struct Node* nextNode = headNode->next;
	if (nextNode != NULL) //防御性编程
	{
		headNode->next = nextNode->next;
		free(nextNode);
		nextNode = NULL;
	}
}

在这里插入图片描述

//7.表尾删除
void deleteByTail(struct Node* headNode)
{
	struct Node* frontNode = NULL;
	struct Node* tailNode = headNode;  //NULL;
	//struct Node* tailNode = headNode->naxt;  会引发中断
	while (tailNode->next != NULL) //NULL->next!=NULL
	{
		frontNode = tailNode;
		tailNode = frontNode->next;
	}
	if (frontNode == NULL) 
	{
		printf("表为NULL,删除失败\n");
	}
	else 
	{
		frontNode->next = NULL;
		free(tailNode);
		tailNode = NULL;
	}
}
//8.指定位置删除
void deleteByAppoin(struct Node* headNode, int posData) 
{
	struct Node* frontNode = headNode;
	struct Node* posNode = headNode->next;
	while (posNode != NULL && posNode->data != posData) 
	{
		frontNode = posNode;
		posNode = frontNode->next;
	}
	if (posNode == NULL) 
	{
		printf("未找到,无法删除!\n");
	}
	else 
	{
		frontNode->next = posNode->next;
		free(posNode);
		posNode = NULL;
	}
}

在这里插入图片描述

//9.遍历--->打印
void printList(struct Node* headNode) 
{
	//为什么指向第二个节点? 因为第一个节点没有存数据
	struct Node* pMove = headNode->next;
	while (pMove != NULL) 
	{
		printf("%d\t", pMove->data);
		pMove = pMove->next;
	}
	printf("\n");
}
int main() 
{
	struct Node* list = createHead();
	for (int i = 0; i < 3; i++) 
	{
		insertByHead(list, i);
	}
	printList(list);
	insertByTail(list, 100);
	printList(list);
	insertByAppoin(list, 99, 100);
	printList(list);
	insertByAppoin(list, 102, 112122);
	insertByAppoin(list, 102, 2);
	printList(list);
	deleteByHead(list);
	printList(list);
	deleteByTail(list);
	printList(list);
	deleteByAppoin(list, 1);
	printList(list);
	return 0;
}

13.链表在项目当中的使用

链表:充当数据的容器
特点:灵活的存储数据
int date 改为 struct name date

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值