C语言基础之结构体

一、结构体

1、结构体概述

将多种数据结构封装在一起 形成新的结构叫结构体

每种数据结构 都有自己独立的空间

结构体的关键字 struct

2、结构体类型的定义方式

(1)先定义结构体类型,再定义结构体变量

struct str
{
    int num;//结构体成员
    char name;
};
struct str array;//定义结构体变量

(2)结构体类型、变量同时定义

struct str
{
    int num;//结构体成员
    char name;
}array;//定义结构体变量
struct str data;//定义结构体新变量

(3)一次性结构体

struct 
{
     int num;   
}asd;//定义变量

3、结构体成员的初始化

(1)结构体初始化

#include <stdio.h>

struct stu
{
    int num;
    char str[128];
    int data[32];
};
int main(int argc, char const *argv[])
{
    struct stu array = {100,"hello",{200,300}};
    printf("%d %s %d\n",array.num, array.str, array.data[1]);
    return 0;
}

(2)清空结构体

#include <stdio.h>
#include <string.h>
struct stu
{
    int num;
    char str[128];
    int data[32];
};
int main(int argc, char const *argv[])
{
    struct stu array;
    memset(&array,0,sizeof(array));
    printf("%d\n",sizeof(array));//260 = 1*4+128+32*4
    return 0;
}

(3)键盘给结构体赋值

#include <stdio.h>
#include <string.h>
struct stu
{
    int num;
    char str[128];
    int data[32];
};
int main(int argc, char const *argv[])
{
    struct stu array;
    memset(&array,0,sizeof(array));
    scanf("%d %s",&array.num, array.str);//键盘获取内容
    printf("%d  %s\n",array.num, array.str);
    return 0;
}
#include <stdio.h>
#include <string.h>
struct date
{
    int num;
    char str[128];
};
int main(int argc, char const *argv[])
{
    struct date arr[3];
    memset(arr,0,sizeof(arr));
    int n = sizeof(arr)/sizeof(arr[0]);

    //获取键盘数组
    printf("请输入%d个学生信息num str\n",n);
    int i = 0;
    for ( i = 0; i < n; i++)
    {
        scanf("%d %s",&arr[i].num,arr[i].str);
    }
    
    for ( i = 0; i < n; i++)
    {
        printf("%d %s\n",arr[i].num,arr[i].str);
    }   
}

4、结构体成员操作

int类型

zxc.num +=100;

char类型

strcpy(zxc.str,"name");

5、相同类型的结构体操作

#include <stdio.h>
#include <string.h>
struct stu
{
    int num;
    char str[128];
    int data[32];
};
int main(int argc, char const *argv[])
{
	struct stu array = {100,"hello",200};
    struct stu array2;
    #if 1
    //方式一;成员逐个赋值
        array2.num = array.num;
        *array2.data = *array.data;
        strcpy(array2.str,array.str);
    #elif 0
    //方式二;相同类型的结构体变量,可以直接赋值
        array2 = array;
    #else
    //方式三
    memcpy(&array2,&array,sizeof(array));

    #endif
    printf("%d %s %d\n",array2.num,array2.str, *array2.data);
    return 0;
}

6、结构体嵌套

#include <stdio.h>
#include <string.h>
struct stu
{
    int num;
    char str[128];
    int data[32];
};
struct date
{
    int year;
    int month;
    int day;
    struct stu ob;
};
int main(int argc, char const *argv[])
{
	struct date lucy = {2023,07,28,{666,"xixi",999}};
    printf("year=%d month=%d day=%d\n",lucy.year,lucy.month,lucy.day);
    printf("str = %s data = %d\n",lucy.ob.str,*lucy.ob.data);
    return 0;
}

结果:

year=2023 month=7 day=28
str = xixi data = 999

7、结构体数组

#include <stdio.h>
#include <string.h>
struct stu
{
    int num;
    char name[128];
};
int main(int argc, char const *argv[])
{
	struct date2 arr[3] = {{100,"qwe"},{232,"asdas"},{7897,"ksjs"}};
    int n = 0;
    n = sizeof(arr)/sizeof(arr[0]);
    
    int i = 0;
    for ( i = 0; i < n; i++)
    {
        printf("%d  %s\n",arr[i].num,arr[i].name);
    } 
    return 0;
}

8、结构体指针变量

结构体指针变量 本质是指针变量 保存的是结构体变量的地址

p->num 根据地址获取num

(*p).num

左边是 地址 使用 ->

左边是 变量 使用 .

#include <stdio.h>
#include <string.h>
struct stu
{
    int num;
    char str[128];
    int data[32];
};
int main(int argc, char const *argv[])
{
	struct stu ob = {100,"xixi",200};
    struct stu *p = &ob;
    printf("%d %s %d\n",p->num,p->str,*(*p).data);
    return 0;
}

9、结构体数组元素的指针

#include <stdio.h>
#include <string.h>
struct data
{
    int num;
    char str[128];
};
void input_stu_data(struct data *arr,int n)
{
    printf("请输入%d个学生信息num arr\n",n);
    int i = 0;
    for ( i = 0; i < n; i++)
    {
        printf("请输入第%d个学生信息:",i+1);
        scanf("%d %s",&(arr+i)->num,arr[i].str);
    }   
    return;
}
void sort_arr_array(struct data *arr ,int n)
{
    int i = 0;
    for ( i = 0; i < n-1; i++)
    {
        int min = i;
        int j = min +1;
        for ( ; j < n; j++)
        {
            if(arr[min].num>arr[j].num)
                min = j;
        }
        if(i != min)
        {
            struct data tmp = arr[min];
            arr[min] = arr[i];
            arr[i] = tmp;
        }
    }
    return;
}
int main(int argc, char const *argv[])
{
    struct data arr[5];
    memset(arr,0,sizeof(arr));

    int n = sizeof(arr)/sizeof(arr[0]);
    //键盘输入
    input_stu_data(arr,n);
    //对结构体数组排序
    sort_arr_array(arr , n);
    int i = 0;
    for ( i = 0; i < n; i++)
    {
        printf("%d %s\n",arr[i].num,arr[i].str);
    }
    return 0;
}

10、结构体的指针指向堆区空间

当结构体有指针成员时须为其申请指针空间

#include <stdio.h>
#include <string.h>
struct stu
{
    int num;
    char *name;
};
int main(int argc, char const *argv[])
{
    struct stu lucy;
    lucy.num = 100;
    lucy.name = (char *)calloc(1,128);//申请空间
    strcpy(lucy.name, "hellow world");
    printf("%d %s\n",lucy.num,lucy.name);
    printf("%c\n",lucy.name[1]);
    lucy.name[1] = 'D';
    printf("%d %s\n",lucy.num,lucy.name);
    //释放lucy.name指向的堆区空间
    if(lucy.name != NULL)
    {
        free(lucy.name);
        lucy.name = NULL;
    }
    return 0;
}

11、结构体深层拷贝

如果结构体中含有指针成员,尽量使用深拷贝

(如果使用浅拷贝,拷贝完成后需要对lucy、ob申请的空间进行释放,由于指向同一空间,就造成空间重复释放,所以使用深拷贝,为ob重新申请一个空间,使用完成后进行释放)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct stu
{
    int num;
    char *name;
};


int main(int argc, char const *argv[])
{
    struct stu lucy;
    struct stu ob;
    lucy.num = 100;
    lucy.name = (char *)calloc(1,128);//申请空间
    ob.name = (char *)calloc(1,128);
    
    ob.num = lucy.num;
    strcpy(ob.name,lucy.name);
    strcpy(lucy.name, "hellow world");
    printf("%d %s\n",lucy.num,lucy.name);
    printf("%c\n",lucy.name[1]);
    
    lucy.name[1] = 'D';
    printf("%d %s\n",lucy.num,lucy.name);
    //释放lucy.name指向的堆区空间
    if(lucy.name != NULL)
    {
        free(lucy.name);
        lucy.name = NULL;
    }
    if(ob.name != NULL)
    {
        free(ob.name);
        ob.name = NULL;
    }
    return 0;
}

如果结构体中没有指针成员 赋值 不会出现浅拷贝。

如果结构体中有指针成员 赋值 容易造成浅拷贝

12、结构体与结构体成员都在堆区

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct stu
{
    int num;
    char *name;
};
int main(int argc, char const *argv[])
{
    //结构体本身在堆区
    struct stu *p = NULL;
    p = (struct stu *)calloc(1,sizeof(struct stu));

    //为结构体指针成员申请空间
    
    p->name = (char *)calloc(1,128);//(*p).name = (char *)calloc(1,128);
    p->num = 100;
    strcpy(p->name,"hello world");
    printf("%d %s\n",p->num,p->name);
    //先释放成员空间
    if(p->name != NULL)
    {
        free(p->name);
        p->name = NULL;
    }
    //在释放结构体空间
    if(p != NULL)
    {
        free(p);
        p = NULL;
    }
} 

13、结构体对齐

(1)结构体自动类型对齐

①确定分配单位(一行分配多少字节)

​ 结构体中最大的基本类型长度决定

②确定成员偏移量

​ 成员偏移量 = 成员本身类型的整数倍

③收尾工作

​ 结构体的总大小 = 分配单位整数倍

#include <stdio.h>
struct array
{
    char str;
    int num;
    char name;
};

int main(int argc, char const *argv[])
{
    struct array A;
    printf("%d\n",sizeof(A));
    printf("%p\n",&A.str);
    printf("%p\n",&A.num);
    printf("%p\n",&A.name);
    return 0;
}

结果:

12
000000000061FDE4
000000000061FDE8
000000000061FDEC

案例1:画出以下结构体的对齐

struct Data
{
    char a;
    short b;
    int c;
    char d;
    short e;
};

在这里插入图片描述

(2)结构体嵌套结构体 自动对齐规则

①确定分配单位(一行分配多少字节)

​ 所有结构体中最大的基本类型长度决定

②确定成员的偏移量

​ 普通成员偏移量 = 成员自身类型的整数倍

​ 结构体成员的偏移量 = 结构体中最大的基本类型整数倍

③收尾工作

​ 结构体成员大小 = 该结构体中最大的基本类型整数倍

​ 结构体的总大小 = 分配单位整倍数

#include <stdio.h>
struct data1
{  
    char str;
    char str2;
    int num;
};
struct data2
{ 
    char str1;
    struct data1 bob;
    short num1;
};

int main(int argc, char const *argv[])
{
    struct data2 bob2;
    printf("%d\n",sizeof(bob2));
    //结构体自动对齐
    printf("%p\n",&bob2.str1);
    printf("%p\n",&bob2.bob.str);
    printf("%p\n",&bob2.bob.str2);
    printf("%p\n",&bob2.bob.num);
    printf("%p\n",&bob2.num1); 
    return 0;
}

结果:

16
000000000061FDE0
000000000061FDE4
000000000061FDE5
000000000061FDE8
000000000061FDEC

(3)结构体强制对齐

​ #prangma pack(value)是指定对齐值

​ valu值为1,2,4,8,16

①确定分配单位(一行分配多少字节)

​ 分配单位 = min(结构体中最大的基本类型,value)

②确定成员的偏移量

​ 成员偏移量 = 成员自身类型的整数倍

③收尾工作

​ 结构体的总大小 = 分配单位整倍数

#include <stdio.h>
#pragma pack(2)
struct array
{
    char str;
    int num;
    char name;
};

int main(int argc, char const *argv[])
{
    struct array A;
    printf("%d\n",sizeof(A));
    printf("%p\n",&A.str);
    printf("%p\n",&A.num);
    printf("%p\n",&A.name);
    return 0;
}

结果:

8
000000000061FDE8
000000000061FDEA
000000000061FDEE

14、位域

在结构体中,以位为单位的成员,称之为位段(位域)

struct A
{
    unsigned int a:2;
    unsigned int b:6;
    unsigned int c:4;
    unsigned int d:3;
    unsigned int e;
}data;

​ a为无符号整型,大小只占2位二进制位

​ 位域可以是unsigned int ,unsigned char

​ 没有非位域隔开的位域 叫相邻位域(a,c)

​ 相邻位域可以压缩

​ 位段的大小不能超过存储单元的大小

​ 不要对位域取地址。

如:a=9 == 1001 ,但a的存储单元只有两位所以只能存储01

​ b=9 == 1001,b的存储单元有6位可以存储1001

#include <stdio.h>

struct A
{
    unsigned int a:2;
    unsigned int b:6;
    unsigned int c:4;
    unsigned char :8;
    unsigned int d:3;
    unsigned int e;
}data;
int main(int argc, char const *argv[])
{
    printf("sizeof(struct A)=%lu\n",sizeof(struct A));
    return 0;
}

无意义位段

#include <stdio.h>
struct A
{
    unsigned char a : 2;
    unsigned char :2;  //两位无意义位段
    unsigned char b : 2;
    unsigned char c : 2;
    //int num;
};

int main(int argc, char const *argv[])
{
    printf("sizeof(struct A)=%lu\n",sizeof(struct A));
}

在这里插入图片描述

#include <stdio.h>
struct B
{
    unsigned char addr : 2;
    unsigned char : 1;
    unsigned char opt :2;
    unsigned char : 1;
    unsigned char data : 2;
};
int main(int argc, char const *argv[])
{
    struct B reg;
    reg.addr = 2;
    reg.opt = 1;
    reg.data = 1;
}

二、共用体

共用体:所有成员共享同一块空间

结构体:所有成员拥有独立空间

union C
{
    int a;
    short b;
    char c;
};

成员a b c共享同一块空间。空间大小 由最大的成员空间决定,即c

union C ob;
ob.a = 10;//空间内容为10
ob.b = 20;//空间内容为20
ob.c = 30;//空间内容为30
ob.a + ob.b + ob.c = 90;

三、枚举enum

枚举:将枚举变量要赋的值一一列举出来

#include <stdio.h>
#include <string.h>

enum ha{data1,data2,data3=14,data4};
int main(int argc, char const *argv[])
{
    enum ha num = data2;
    printf("%d\n",num);
}

枚举默认从0开始依次递增即,data1 = 0 ; data2 = 1 ; data3 = 14 ; data4 = 15 ;

四、如有错误欢迎指正
如要转发请告知

  • 18
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值