知识点一:结构体定义
头文件:
#include<string.h>结构体中的成员拥有
独立的空间
struct stu
{
int num;
char name[32];
int age;
}; //一定要记得有;号
//struct:结构体关键字
//stu:结构体类型名,指定一个结构体类型,它相当于一个模型,但其中并无具体数据,系统对之也不分配实际内存单元
//使用结构体类型必须是struct stu
//num name age叫做结构体中的成员
//定义结构体类型的时候不要给成员赋值(重要)
//定义结构体类型的时候 并没有分配空间 所以不能给成员赋值
结构体定义形式
//先定义类型再定义变量(推荐)
struct stu
{
int num;
char name[32];
int age;
};
struct stu lucy;
//定义类型的同时定义变量
struct stu
{
int num;
char name[32];
int age;
}lucy;
struct stu bob;
//定义一次性结构体
struct
{
int num;
char name[32];
int age;
}lucy;
//不能定义其他变量
结构体变量定义和初始化
struct stu
{
int num; //4B
char name [32];//32B
int age; //4B
};
void test1(void)
{
//用结构体类型定义一个变量
struct stu lucy; //lucy 是局部变量 lucy的成员内容不确定
//结构体变量初始化,初始化顺序必须和结构体成员的顺序一致
//struct stu lucy = {100,"德玛西亚",18};
//printf(" num = %d,name = %s,age = %d\n", lucy.num,lucy.name,lucy.age);
//清空结构体变量
//memset(&lucy,0,sizeof(lucy));
//通过结构体变量访问结构体中的成员(访问时一定要遵循成员自身的类型)
lucy.num = 100;
strcpy (lucy.name,"德玛西亚");
lucy.age = 18;
printf("num = %d\n",lucy.num); //100
printf("name = %s\n",lucy.name); //德玛西亚
printf("age = %d\n",lucy.age); //18
}
知识点二:结构体变量获取键盘输入
struct stu
{
int num; //4B
char name [32]; //32B
int age; //4B
};
void test1(void)
{
struct stu lucy;
memset(&lucy,0,sizeof(lucy));
printf("请输入num name_age:") ;
//&lucy.num取的是num成员地址
scanf("%d %s %d",&lucy.num,&lucy.name,&lucy.age);
printf("num = %d,name = %s,age = %d\n",lucy.num,lucy.name,lucy.age);
}
知识点三:结构体变量之间的赋值
memcpy:内存块中的数据从一个地方复制到另一个地方
memcpy(void *str1, const void *str2, size_t n);
参数说明:
str1:指向用于存储复制内容的目标数组,类型强制转换为 void* 指针
str2:指向要复制的数据源,类型强制转换为 void* 指针
n:要被复制的字节数
struct stu
{
int num; //4B
char name [32]; //32B
int age; //4B
};
void test1(void)
{
struct stu lucy = {100,"小法",18} ;
struct stu bob;
//需求将lucy的值赋值bob
//方式一:逐个成员赋值
//bob.num = lucy.num;
//strcpy(bob.name,lucy.name);
//bob.age = lucy.age;
//方式二:相同类型的结构体变量 可以直接赋值(推荐)
//bob = lucy;
//方法三:方法二的底层实现
memcpy(&bob, &lucy,sizeof(struct stu));
printf("num = %d,name = %s,age = %d\n",bob.num,bob.name,bob.age);
}
知识点四:结构体数组
struct stu
{
int num; //4B
char name [32]; //32B
int age; //4B
};
void test1(void)
{
struct stu arr[5]={
{100,"小法",18},
{101,"德玛西亚",19},
{102,"盲僧",20},
{103,"快乐风男",30},{104,"提莫",8}};
int n = sizeof(arr)/sizeof(arr[0]);
int i = 0;
for(i = 0;i < n;i++)
{
printf("num = %d,name = %s,age = %d\n",arr[i].num, arr[i].name,arr[i].age);
}
}
知识点五:typedef 给已有的类型取个别名
1、给int取个别名INT32
#include<stdio.h>
typedef int INT32;
void test1()
{
INT32 num = 10;
printf("num = %d\n",num); //10
}
2、给数组取个别名
//ARR_TYPE 就是数组类型,该数组必须有5个元素 每个元素为int
typedef int ARR_TYPE[5];
void test1()
{
ARR_TYPE arr = {10, 20, 30, 40, 50}; //arr就是一个拥有5个元素每个元素为int的数
int i = 0;
for(i = 0;i < 5;i++)
{
printf("%d ",arr[i]);
}
printf("\n") ;
}
3、给指针取别名
typedef int *P_TYPE;
void test1()
{
int num = 10;
P_TYPE p = # //P_TYPE p == int *p
printf("*p = %d\n", *p); //10
}
4、给函数指针取别名
int my_add(int x,int y)
{
return x+y;
}
//FUN_P是一个函数指针类型 该函数必须有两个int形参以及一个int返回值
typedef int (*FUN_P)(int x, int y);
void test1()
{
FUN_P p = my_add;
printf("%d\n",p(100,200)); //300;
}
5、给结构体类型取个别名
typedef struct stu
{
int num;
char name[32];
int age;
}STU; //STU == struct stu
void test1()
{
STU lucy = {100,"lucy",18};
}
知识点六:结构体指针
typedef struct stu
{
int num;
char name[16];
int age;
}STU; //STU是结构体类型
void test1()
{
STU lucy = {100,"lucy",18};
STU *p = &lucy;
printf("num = %d,name = %s,age = %d\n",lucy.num,lucy.name,lucy.age);
printf("num = %d,name = %s,age = %d\n",(*p).num,(*p).name,(*p).age);
printf("num = %d,name = %s,age = %d\n", p->num, p->name, p->age);
printf("num = %d\n", (&lucy)->num);
}
知识点七:结构体指针作为函数参数
#include<stdio.h>
#include<string.h>
typedef struct stu
{
int num;
char name[16];
int age;
}STU; //STU是结构体类型
void mySetSTUData(STU *p) //p=&lucy
{
printf("请输入一个学生的信息num name age\n");
scanf("%d %s %d",&p->num,p->name,&p->age);
return;
}
void myPrintSTUData(const STU *p) //tmp =lucy *p只读
{
//constSTU*p不允许用户借助p修改 p所指向的空间内容
printf("num = %d,name = %s,age = %d\n",p->num,p->name,p->age);
}
void test1()
{
STU lucy;
memset(&lucy,0,sizeof(lucy));
//定义一个函数给lucy的成员获取键盘输入
mySetSTUData(&lucy);
//定义一个函数 打印lucy的成员信息
myPrintSTUData(&lucy);
}
知识点八:结构体内存对齐
typedef struct
{
int a;
char b;
short c;
char d;
}DATA;
void test06()
{
DATA d;
printf("%d\n", sizeof(DATA));
printf("%u\n", &d.a);
printf("%u\n", &d.b);
printf("%u\n", &d.c);
printf("%u\n", &d.d);
}
知识点九:结构体嵌套结构体
typedef struct
{
int x;
int y;
}DATA2;
typedef struct
{
int a;
int b;
DATA2 c; //结构体变量c作为了DATA3的成员叫结构体嵌套结构体
}DATA3;
void test1()
{
//DATA3 data = {10, 20, 30, 40};
DATA3 data = {10,20, {30,40}}; //推荐
printf("a = %d\n",data.a);
printf("b = %d\n",data.b);
printf("x = %d\n",data.c.x); //访问最底层
printf("y = %d\n",data.c.y);
}
知识点十:嵌套结构体内存对齐
typedef struct
{
short d;
char e;
}DATA2;
typedef struct
{
short a;
int b;
DATA2 c;
char f;
}DATA;
void test1()
{
printf("%d\n", sizeof(DATA)); //16
DATA data;
printf ("a:%u\n", &data.a); //1703628
printf("b:%u\n", &data.b); //1703632
printf("c中d: %u\n",&data.c.d); //1703636
printf("c中e: %u\n", &data.c.e); //1703638
printf("f:%u\n",&data.f); //1703640
}
知识点十一:强制类型对齐
格式:
#pragma pack (value) //value:指定对齐值
//value只能是:1 2 4 8...(指定对齐值与数据类型对齐值相比取较小值)
//设为1:则short、 int、 float等均为1,设为2:则char仍为1,short为2,int变为2
//指定对齐规则
#pragma pack(2)
typedef struct
{
char a;
int b;
short C;
}DATA1;
void test1()
{
printf ("%d\n",sizeof(DATA1)); //8
return;
}
知识点十二:结构的浅拷贝和深拷贝
1、指针变量作为结构体的成员
typedef struct
{
int num;
char *name; //指针变量作为结构体的成员
}DATA;
void test01()
{
DATA data = {100, "hehehehaha"};
printf("%d\n",sizeof(DATA)); //8字节
printf("num = %d\n" ,data.num);
//指针变量作为结构体的成员保存的是空间的地址
printf("name = %s\n", data.name);
}
2、指针变量作为结构体的成员 操作前 必须有合法空间
void test02()
{
DATA data;
printf( "%d\n",sizeof(DATA));
printf("num = %d\n", data.num);
//指针变量作为结构体的成员操作前必须有合法的空间
//data.name = "hehe";
//给name事先申请一块堆区空间
data.name = (char *)calloc(1,10);
strcpy(data.name,"hahaha");
printf("name = %s\n", data.name );
//如果name指向堆区空间一定要记得释放
if(data. name != NULL )
{
free(data. name);
data. name = NULL;
}
}
3、指针变量作为结构体成员,结构体变量间的赋值操作容易导致“浅拷贝”发生
两个结构体变量中的指针成员指向同一块堆区空间。运行结果:出现段错误
void test03()
{
DATA data1;
DATA data2;
data1.num = 100;
data1.name = (char *)calloc(1,10);
strcpy(data1.name,"my data");
//指针变量作为结构体的成员结构体变量间的赋值操作容易导致"浅拷贝”发生
data2 = data1; //"浅拷贝”
printf("data2: num = %d,name = %s\n", data2.num,data2.name);
if(datal.name != NULL)
{
free(data1.name);
data1.name = NULL;
}
if( data2.name != NULL)
{
free(data2.name);
data2.name = NULL;
}
}
4、深拷贝
前提:是指针变量作为结构体的成员
两个结构体变量中的指针成员指向各自的堆区空间
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct
{
int num;
char *name;
}DATA;
void test01()
{
DATA datal ;
DATA data2 ;
datal.num = 100;
datal.name = (char *)calloc(1,12);
strcpy (datal.name,"my data");
data2.num = datal.num;
//为结构体变量 申请 独立空间
data2.name = (char *)calloc(1,12);
strcpy(data2.name,datal.name) ;
printf("data1:num = %d,name = %s\n",datal.num,datal.name);
printf("data2:num = %d,name = %s\n",data2.num,data2.name);
if (datal.name!=NULL)
{
free(datal.name);
datal.name = NULL;
}
if (data2.name!=NULL)
{
free(data2.name);
data2.name = NULL;
}
}
知识点十三:位段
结构体中以位为单位来指定其成员所占内存长度,以位为单位的成员称为“位段”或称“位域”
位段定义与使用
//位段 一般只考虑unsigned int类型也可以考虑unsigned char
typedef struct
{
unsigned char a:2;
unsigned char b:2;
//相邻位域可以压缩(压缩的位数 不能超过成员自身大小)
unsigned char :4; //无意义的位段
}DATA3;
void test1()
{
printf ("%d\n",sizeof(DATA3)); //1
DATA3 data;
memset (&data,0,1);
//位段不能取地址
//printf("%p\n",&data.a);
//位段的赋值不要操作位段的大小a:2
data.a = 6; //0110
printf("%u\n",data.a); //2
data.a = 0; //00
data.b = 3; //11
printf("%d\n", sizeof(DATA3));
printf("%#x\n",*(unsigned char *)&data); //1100 0000/ /0xc0 a是高位,b是低位
return;
}
另起一个位段
typedef struct
{
unsigned char a:2; //00
unsigned char :0; //另起一个位段
unsigned char b:2; //11
}DATA4;
void test1()
{
printf ("%d\n",sizeof(DATA4));
return;
}
知识点十四:共用体
所有的成员
共享同一份空间,从空间读取的字节数由成员自身类型决定
union data
{
char a;
short b;
int c;
};
void test1()
{
printf("%d\n",sizeof(union data)); //4
union data A;
A.a = 10;
A.b = 20;
A.c = 30;
//共用体是最后一次 赋值有效(不严谨)
printf("%d\n", A.a + A.b + A.c); //90
A.c = 0x01020304;
A.b = 0x0102;
A.a = 0x01;
printf("%#x\n",A.a + A.b + A.c); //0x01020203
}
知识点十五:枚举
将变量的值一一列举出来,在枚举值表中应列出所有可用值,也称为枚举元素
枚举变量的值(默认从零开始顺序加1)只能取枚举值所列元素
//枚举类型定义:
enum 枚举名
{
枚举值表
};
//枚举列表的值:默认从0开始
enum POKER {HONGTAO,HEITAO=30,MEIHUA=40,FANGKUAI};
void test1()
{
//poker_ color的取值为HONGTAO,HEITAO, MEIHUA, FANGKUAI中某一个
enum POKER poker_color = HEITAO;
printf("poker_ olor = %d\n",poker_color); //30
printf("HONGTAO = %d\n",HONGTAO); //0
printf("HEITAO = %d\n",HEITAO); //30
printf("MEIHUA = %d\n",MEIHUA); //40
printf("FANGKUAI = %d\n",FANGKUAI); //41
}