10-结构体、共用体、枚举

130 阅读10分钟

知识点一:结构体定义

头文件:#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 = &num;    //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
}