结构体
1. 基本操作
1.1 定义变量
- 方法一. 先定义类型,再定义变量
struct Student{
char *name;
int age;
int score;
};
void main()
{
struct Student st1;
system("pause");
}
对于结构体类型的定义,只是指定了一种新的数据类型,就好比告诉编译器,现在有一个新的数据类型,它对应的内存大小是多少,但实际上并没有分配内存空间。
直到定义变量以后才真正分配内存空间
还可以重定义结构体类型名,如下所示:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
typedef struct Student{
char *name;
int age;
int score;
}student;
void main()
{
student st1;
system("pause");
}
- 方法二. 定义结构体类型(名)的同时定义结构体变量
struct Student{
char *name;
int age;
int score;
}st2;
void main()
{
system("pause");
}
此时的t2是一个struct Student类型的全局变量
- 方法三. 不定义结构体类型名直接定义结构体变量
struct {
char *name;
int age;
int score;
}st2;
1.2 初始化变量
- 方法一. 定义好变量以后初始化
typedef struct Teacher
{
char name[64];
int age ;
int id;
}Teacher;
Teacher t3 = {"aaaa", 31, 01};
- 方法二.
struct Student2
{
char name[64];
int aga ;
}s5 = {"names", 21};
- 方法三.
struct
{
char name[64];
int aga ;
}s6 = {"names", 21};
1.3 访问成员
- 使用
.
运算符访问结构体成员 - 使用
->
运算符访问结构体成员
.
操作符符和->
运算符 是 干什么?他们并没有操作内存
只是寻址操作:计算成员变量 相对于 结构体大变量(起始地址)的 偏移量 ===》计算CPU中进行。
2.结构体变量作函数参数 VS 结构体指针作函数参数
void copyTeacher(Teacher to, Teacher from )
{
to = from;
}
void copyTeacher02(Teacher *to, Teacher *from )
{
//(*to) = (*from);
*to = *from;
}
void main111()
{
Teacher t1 = {"aaaa", 32, 02};
Teacher t2;
Teacher t3;
memset(&t3, 0, sizeof(t3));
t2 = t1; //=号操作下 编译器的行为
//编译器给我们提供 简单 =号 赋值操作 。。。我们要顺从
printf("t2.name:%s \n", t2.name);
printf("t2.age:%d \n", t2.age);
copyTeacher(t3, t1);
printf("copyTeacher() after \n");
printf("t3.name:%s \n", t3.name);
printf("t3.age:%d \n", t3.age);
printf("hello...\n");
copyTeacher02(&t3, &t1);
printf("copyTeacher02() after \n");
printf("t3.name:%s \n", t3.name);
printf("t3.age:%d \n", t3.age);
printf("hello...\n");
system("pause");
return ;
}
3.结构体指针的输入输出模型
- 简单的输出模型
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
typedef struct Teacher
{
char name[64];
int age ;
int id;
}Teacher;
void printTeacher(Teacher *array, int num)
{
int i = 0;
for (i=0; i<num; i++)
{
printf("age:%d \n", array[i].age);
}
}
void sortTeacer(Teacher *array, int num)
{
int i,j;
Teacher tmp;
for (i=0; i<num; i++)
{
for (j=i+1; j<num; j++)
{
if (array[i].age > array[j].age)
{
tmp = array[i]; //=号操作 赋值操作
array[i] = array[j];
array[j] = tmp;
}
}
}
}
// 结构体数组 3 输入老师的年龄,排序
void main22()
{
int i = 0;
Teacher Array[3]; //在stack 分配内存
int num = 3;
for (i=0; i<num; i++)
{
printf("\nplease enter age:");
scanf("%d", &(Array[i].age) );
}
//打印老师年龄
// for (i=0; i<num; i++)
// {
// printf("age:%d \n", Array[i].age);
// }
printTeacher(Array, num);
sortTeacer(Array, num);
printf("排序之后\n");
printTeacher(Array, num);
printf("hello...\n");
system("pause");
return ;
}
Teacher * createTeacher(int num)
{
Teacher * tmp = NULL;
tmp = (Teacher *)malloc(sizeof(Teacher) * num); // Teacher Array[3]
if (tmp == NULL)
{
return NULL;
}
return tmp; //
}
void FreeTeacher(Teacher *p)
{
if (p != NULL)
{
free(p);
}
}
void main233()
{
int i = 0;
//Teacher Array[3]; //在stack 分配内存
int num = 3;
Teacher *pArray = NULL;
pArray = createTeacher(num);
for (i=0; i<num; i++)
{
printf("\nplease enter age:");
scanf("%d", & (pArray[i].age) );
}
//打印老师年龄
// for (i=0; i<num; i++)
// {
// printf("age:%d \n", Array[i].age);
// }
printTeacher(pArray, num);
sortTeacer(pArray, num);
printf("排序之后\n");
printTeacher(pArray, num);
FreeTeacher(pArray);
printf("hello...\n");
system("pause");
return ;
}
- 高级一点的输出模型
int createTeacher02(Teacher **pT, int num)
就是把简单输出模型的返回值改为int类型,将在堆空间分配的内存从函数参数用二级指针甩出来。
- 结构体中套一级指针
记得为结构体里面的一级指针分配内存空间
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
typedef struct _student{
char name[64];
char *alias;
int age;
}student;
int createStudent(student **ptr,int count)
{
int ret = 0;
int i = 0;
student * tmp = NULL;
tmp = (student*)malloc(count*sizeof(student));//申请结构体变量所需的内存空间,相当于一个结构体数组
if (tmp == NULL)//申请失败
{
return -1;
}
memset(tmp, 0, count*sizeof(student));//初始化新分配到的内存空间
for (i = 0; i < count;i++){//逐个对结构体变量中的一级指针成员申请空间,否则之前申请的空间只是一个指针变量,这个指针变量并没有实际的内存空间
tmp[i].alias = (char *)malloc(64);
if (NULL == tmp[i].alias){
return -1;
}
}
*ptr = tmp;//通过函数参数甩出内存空间
return ret;
}
int freeStudent(student **ptr,int count)
{
int ret = 0;
int i = 0;
student * tmp = NULL;
if (ptr == NULL){
return -1;
}
tmp = *ptr;
for (i = 0; i < count; i++)
{
if (tmp[i].alias != NULL){
free(tmp[i].alias);
tmp[i].alias = NULL;
}
}
free(tmp);
*ptr = NULL;
return ret;
}
void printStudent(student *ptr, int count)
{
int i = 0;
for (i = 0; i < count; i++){//逐个打印结构体变量的age成员
printf("age:%d\n", ptr[i].age);
}
}
//依据年龄排序
void sortStudent(student *ptr, int count)
{
int i = 0, j = 0;
student tmp;
for (i = 0; i < count;i++)
{
for (j = i + 1; j < count; j++)
{
if (ptr[i].age > ptr[j].age)
{
tmp = ptr[i];
ptr[i] = ptr[j];
ptr[j] = tmp;
}
}
}
}
int main()
{
int ret = 0;
student *pStudent = NULL;
int num = 3,i = 0;
ret = createStudent(&pStudent, num);//创造一个结构体数组
if (-1 == ret){
return -1;
}
for (i = 0; i < num; i++)//依次输入结构体成员变量的值
{
printf("\nplease enter age:");
scanf("%d", &(pStudent[i].age));
printf("\nplease enter name:");
scanf("%s", pStudent[i].name); //向指针所指的内存空间copy数据
printf("\nplease enter alias:");
scanf("%s", pStudent[i].alias); //向指针所指的内存空间copy数据
}
printf("排序之前\n");
printStudent(pStudent, num);
sortStudent(pStudent, num);
printf("排序之后\n");
printStudent(pStudent, num);
freeStudent(&pStudent,num);
system("pause");
return ret;
}
- 结构体中套二级指针
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define FRIEND_NUM 3
typedef struct _student{
char name[64];
char *alias;
char **friends;
int age;
}student;
int createStudent(student **ptr,int count)
{
int ret = 0;
int i = 0, j = 0;
student * tmp = NULL;
char **ptmp = NULL;
tmp = (student*)malloc(count*sizeof(student));//申请结构体变量所需的内存空间,相当于一个结构体数组
if (tmp == NULL)//申请失败
{
return -1;
}
memset(tmp, 0, count*sizeof(student));//初始化新分配到的内存空间
for (i = 0; i < count;i++){//逐个对结构体变量中的一级指针成员申请空间,否则之前申请的空间只是一个指针变量,这个指针变量并没有实际的内存空间
/*一级指针内存模型*/
tmp[i].alias = (char *)malloc(64);
if (NULL == tmp[i].alias){
return -1;
}
/*二级指针第三种内存模型*/
ptmp = (char**)malloc(FRIEND_NUM*sizeof(char*));//二级指针指向一个二维数组,第一维是char*
if (ptmp == NULL)
{
return -1;
}
for (j = 0; j < FRIEND_NUM; j++)
{
ptmp[j] = (char*)malloc(64);//第一维的每一个char*指向一个malloc空间
}
tmp[i].friends = ptmp;//将二级指针抛出给已分配好的结构体二级指针成员变量
}
*ptr = tmp;//通过函数参数甩出内存空间
return ret;
}
int freeStudent(student **ptr,int count)
{
int ret = 0;
int i = 0,j = 0;
char ** ptmp = NULL;
student * tmp = NULL;
if (ptr == NULL){
return -1;
}
tmp = *ptr;
for (i = 0; i < count; i++)
{
if (tmp[i].alias != NULL){//释放一级指针
free(tmp[i].alias);
tmp[i].alias = NULL;
}
if (tmp[i].friends != NULL)//释放二级指针
{
ptmp = tmp[i].friends;
for (j = 0; j < FRIEND_NUM; j++)
{
if (ptmp[j] != NULL)
{
free(ptmp[j]);
ptmp[j] = NULL;
}
}
free(ptmp);
tmp[i].friends = NULL;//注意ptmp只是一个临时变量,不能修改原结构体成员的值
}
}
free(tmp);
*ptr = NULL;//修改主调函数的一级指针要使用二级指针
return ret;
}
void printStudent(student *ptr, int count)
{
int i = 0;
for (i = 0; i < count; i++){//逐个打印结构体变量的age成员
printf("age:%d\n", ptr[i].age);
}
}
//依据年龄排序
void sortStudent(student *ptr, int count)
{
int i = 0, j = 0;
student tmp;
for (i = 0; i < count;i++)
{
for (j = i + 1; j < count; j++)
{
if (ptr[i].age > ptr[j].age)
{
tmp = ptr[i];
ptr[i] = ptr[j];
ptr[j] = tmp;
}
}
}
}
int main()
{
int ret = 0;
student *pStudent = NULL;
int num = 3,i = 0,j = 0;
ret = createStudent(&pStudent, num);//创造一个结构体数组
if (-1 == ret){
return -1;
}
for (i = 0; i < num; i++)//依次输入结构体成员变量的值
{
printf("\nplease enter age:");
scanf("%d", &(pStudent[i].age));
//printf("\nplease enter name:");
//scanf("%s", pStudent[i].name); //向指针所指的内存空间copy数据
//printf("\nplease enter alias:");
//scanf("%s", pStudent[i].alias); //向指针所指的内存空间copy数据
for (j = 0; j < FRIEND_NUM; j++)//给二级指针指向的内存空间的一级指针指向的内存空间赋值
{
printf("please enter student name:");
scanf("%s", pStudent[i].friends[j]);
}
}
printf("排序之前\n");
printStudent(pStudent, num);
sortStudent(pStudent, num);
printf("排序之后\n");
printStudent(pStudent, num);
freeStudent(&pStudent,num);
system("pause");
return ret;
}
4. 结构体的深浅拷贝
- 编译器的等号操作只会把指针变量的值拷贝,不会把指针变量指向的内存空间拷贝过去,就是所谓的浅拷贝(只进行简单的值拷贝操作)。
- memcpy函数也是浅拷贝
- 结构体里面出现一级或者二级指针会出现浅拷贝
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct __teacher
{
char name[64];
int age;
char *alias;
}Teacher;
void copyTeacher(Teacher *to,Teacher *from)
{
//*to = *from;//浅拷贝
memcpy(to, from, sizeof(Teacher));//浅拷贝
}
int main(void)
{
Teacher t1;
Teacher t2;
strcpy(t1.name,"name1");
t1.alias = (char*)malloc(100);
strcpy(t1.alias,"ssss");
copyTeacher(&t2, &t1);
if (t1.alias != NULL)
{
free(t1.alias);
t1.alias = NULL;
}
if (t2.alias != NULL)//运行到这里由于前面已经free,这里二次free会出错
{
free(t2.alias);
t2.alias = NULL;
}
system("PAUSE");
return 1;
}
- 要进行深拷贝只能是显示的分配内存,即在堆里面再开辟一块内存给另一个结构体的指针成员使用,所以可以进行二次释放。主要代码如下:
void copyTeacher(Teacher *to,Teacher *from)
{
*to = *from;
to->alias = (char *)malloc(100);
strcpy(to->alias,from->alias);
//memcpy(to, from, sizeof(Teacher));
}
5. 结构体中的偏移量
将结构体变量映射到内存0地址处开始去观察内存布局(求出偏移量);
一旦结构体定义下来,则其成员的内存布局就定下来了;
可以通过结构体成员的地址和其在结构体中的偏移量反推出整个结构体的起始地址;
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct __teacher
{
char name[64];//64,偏移量为0
int age;//4
int p;//4
char *alias;//4
}Teacher;
int main(void)
{
Teacher t1;
Teacher *p = NULL;
int offset = 0;
offset = (int)&(p->age);
printf("age:%d\n",offset);
offset = (int)(&(((Teacher*)0)->age));//常用来求出某个成员在结构体中的偏移量
printf("age:%d\n", offset);
offset = (int)&(p->alias);
printf("alias:%d\n", offset);
offset = (int)&(p->name);
printf("name:%d\n", offset);
offset = (int)&(p->p);
printf("p:%d\n", offset);
system("PAUSE");
return 1;
}