c语言十九:结构体

一 结构体数据类型的三种定义方式

1.1 先定义类型,再定义变量(推荐)

在这里插入图片描述

1.2 定义类型的同时定义变量

在这里插入图片描述

1.3 定义一次性结构体

在这里插入图片描述

1.4 注意事项

1. struct是结构体关键字;
2. stu是结构体类型名;
3. num,name,age是结构体中的成员;
4. 定义结构体类型的时候,不要给成员赋值;
5. 定义结构体类型的时候,并没有分配内存空间,所以不要给成员赋值;
6. 结构体中的成员拥有独立的内存空间;
struct stu
{
        int num;
        char name[32];
        int age;
};

struct stu andy,tony;

二 结构体变量的初始化

初始化的顺序必须和结构体成员的顺序一致

[root@ansible9 ~]# cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stu
{
	int num;
	char name[32];
	int age;
};

struct stu andy={200,"andy",18};

int main(int arg, char *argv[])
{
	printf("%d\n",andy.num);
	printf("%s\n",andy.name);
	printf("%d\n",andy.age);
}
[root@ansible9 ~]# gcc test.c
[root@ansible9 ~]# ./a.out 
200
andy
18

三 结构体变量成员值的清0(用memset)

[root@ansible9 ~]# cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stu
{
	int num;
	char name[32];
	int age;
};


int main(int arg, char *argv[])
{
	struct stu andy={200,"andy",18};
	memset(&andy,0,(int)sizeof(struct stu));
	printf("%d\n",andy.num);
	printf("%s\n",andy.name);
	printf("%d\n",andy.age);
}
[root@ansible9 ~]# ./a.out 
0

0

四 结构体变量的成员从键盘获取值

[root@ansible9 ~]# cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stu
{
	int num;
	char name[32];
	int age;
};


int main(int arg, char *argv[])
{
	struct stu andy;
	memset(&andy,0,(int)sizeof(struct stu));
	printf("请输入num name age\n");
	scanf("%d %s %d",&andy.num,andy.name,&andy.age);
	printf("%d\n",andy.num);
	printf("%s\n",andy.name);
	printf("%d\n",andy.age);
}
[root@ansible9 ~]# gcc test.c
[root@ansible9 ~]# ./a.out 
请输入num name age
100 tony 38
100
tony
38

五 相同结构体类型的变量之间赋值

5.1 一个成员一个成员逐个赋值

在这里插入图片描述

5.2 用=整体赋值

在这里插入图片描述

5.3 用memcpy内存数据直接拷贝赋值

在这里插入图片描述

六 结构体数组

在这里插入图片描述

6.1 结构体数组的初始化

在这里插入图片描述

七 定义一个结构体数组,从键盘为每个数组元素的每个成员赋值

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stu
{
        int num;
        char name[32];
        int age;
};


int main(int arg, char *argv[])
{
        struct stu arr[5];
        //清空数组
        memset(arr,0,(int)sizeof(arr));
        //数组元素个数n
        int n=(int)sizeof(arr) / (int)sizeof(arr[0]);
        int i=0;
        printf("请输入%d个学生的信息:\n",n);
        for(i=0;i<n;i++)
        {
                scanf("%d %s %d",&arr[i].num,arr[i].name,&arr[i].age);
        }
}

八 结构体指针

8.1 定义一个结构体指针

1. 先定义一个指针变量: *p
2. 再定义一个指针变量要指向的变量,即结构体变量: struct stu a
3. 从上到下替换: struct stu *p

在这里插入图片描述

8.2 用运算符. 或运算符-> 取结构体变量成员的值

8.2.1 普通结构体变量用运算符. 来获取成员的值

在这里插入图片描述

8.2.2 结构体变量的地址用运算符->来获取成员的值

在这里插入图片描述

九 结构体的内存对齐

9.1 问题来源

struct data
{
	char c;
	int i;
}

由于32位的cpu一次性读取4字节内存数据。所以对于上面的结构体,有两种内存排列方式,每种方式cpu读取结构体成员的读取次数不同:

方式一:
在这里插入图片描述

cpu读取变量c时,需要一个cpu周期,提取0x01     0x02      0x03        0x04, 只要0x01
cpu读取变量i时,需要两个cpu周期:
                           第一周期: 提取0x01 0x02 0x03 0x04, 只要0x02 0x03 0x04
                           第二周期: 提取0x05 0x06 0x07 0x08, 只要0x05

方式二:
在这里插入图片描述

cpu读取变量c时,需要一个cpu周期,提取0x01     0x02      0x03        0x04, 只要0x01
cpu读取变量i时,需要一个cpu周期:提取0x05     0x06      0x07        0x08, 四字节全要

总结:方法二是用内存空间换了cpu的执行时间

9.2 结构体的默认对齐规则

规则:
1. 确定分配单位:每一行应该分配的字节数,他由结构体中最大的基本数据类型的长度决定;
2. 确定成员的起始位置的偏移量:成员的基本类型的整数(0-n)倍;
3. 收尾工作:结构体总大小=分配单位的整数倍;

9.2.1 例子1

struct data
{
	char c;
	int i;
};
  1. 分配单位: 4字节
  2. 确定各个成员的起始位置偏移量
    在这里插入图片描述
[root@ansible9 ~]# cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct data 
{
	char c;
	int i;
};
struct data d;
int main(int arg, char *argv[])
{
	printf("结构体变量的起始地址:%u\n",&d);
	printf("结构体变量所占内存大小:%d\n",(int)sizeof(d));
	printf("成员c的起始地址: %u\n",&d.c);
	printf("成员i的起始地址: %u\n",&d.i);
}
[root@ansible9 ~]# ./a.out 
结构体变量的起始地址:6295600
结构体变量所占内存大小:8
成员c的起始地址: 6295600
成员i的起始地址: 6295604

9.2.2 例子2

struct data
{
	int a;
	char b;
	short c;
	char d;
};
  1. 分配单位: 4字节
  2. 确定各个成员的起始位置偏移量

在这里插入图片描述

[root@ansible9 ~]# cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
	int a;
	char b;
	short c;
	char d;
} DATA;
DATA data;
int main(int arg, char *argv[])
{
	printf("结构体变量的起始地址:%u\n",&data);
	printf("结构体变量所占内存大小:%d\n",(int)sizeof(data));
	printf("成员a的起始地址: %u\n",&data.a);
	printf("成员b的起始地址: %u\n",&data.b);
	printf("成员c的起始地址: %u\n",&data.c);
	printf("成员d的起始地址: %u\n",&data.d);
}
[root@ansible9 ~]# gcc test.c
[root@ansible9 ~]# ./a.out 
结构体变量的起始地址:6295600
结构体变量所占内存大小:12
成员a的起始地址: 6295600
成员b的起始地址: 6295604
成员c的起始地址: 6295606
成员d的起始地址: 6295608

9.2.3 例子3

struct data
{
	char a;
	short b;
	short c;
	int d;
	char e;
};
  1. 分配单位: 4字节
  2. 确定各个成员的起始位置偏移量
    在这里插入图片描述
[root@ansible9 ~]# cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
	char a;
	short b;
	short c;
	int d;
	char e;
} DATA;
DATA data;
int main(int arg, char *argv[])
{
	printf("结构体变量的起始地址:%u\n",&data);
	printf("结构体变量所占内存大小:%d\n",(int)sizeof(data));
	printf("成员a的起始地址: %u\n",&data.a);
	printf("成员b的起始地址: %u\n",&data.b);
	printf("成员c的起始地址: %u\n",&data.c);
	printf("成员d的起始地址: %u\n",&data.d);
	printf("成员e的起始地址: %u\n",&data.e);
}
[root@ansible9 ~]# gcc test.c
[root@ansible9 ~]# ./a.out 
结构体变量的起始地址:6295616
结构体变量所占内存大小:16
成员a的起始地址: 6295616
成员b的起始地址: 6295618
成员c的起始地址: 6295620
成员d的起始地址: 6295624
成员e的起始地址: 6295628

十 结构体嵌套结构体

在这里插入图片描述

十一 结构体嵌套结构体的默认内存对齐

对齐规则:
1. 确定分配单位:每一行应该分配的字节数,由所有的结构体中最大的基本类型长度决定;
2. 确定成员的偏移量=自身类型的整数(0-n)倍:
   2.1 普通成员的偏移量=自身类型的整数(0-n)倍
   2.2 结构体类型成员的偏移量=这个结构体中最大的基本类型的长度的整数(0-n)倍
	 2.3 被嵌套的结构体的成员的偏移量是相对于被嵌套的结构体的
3. 收尾工作:结构体的总大小=分配单位的整数倍
被嵌套结构体的总大小=被嵌套的结构体里面最大的基本数据类型的整数倍
typedef struct 
{
	short d;
	char e;
} DATA2;

typedef struct
{
	short a;
	int b;
	DATA2 c;
	char f;
} DATA1
  1. 分配单位:4字节
  2. 确定成员的偏移量:a的偏移量,b的,c的,d的,e的,f的

在这里插入图片描述

[root@ansible9 ~]# cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
	short d;
	char e;
} DATA2;
typedef struct
{
	short a;
	int b;
	DATA2 c;
	char f;
} DATA1;

DATA1 data;
int main(int arg, char *argv[])
{
	printf("一共%d字节\n",(int)sizeof(DATA1));
	printf("a的地址%u\n",&data.a);
	printf("b的地址%u\n",&data.b);
	printf("c的地址%u\n",&data.c);
	printf("d的地址%u\n",&data.c.d);
	printf("e的地址%u\n",&data.c.e);
	printf("f的地址%u\n",&data.f);
}
[root@ansible9 ~]# gcc test.c
[root@ansible9 ~]# ./a.out 
一共16字节
a的地址6295616
b的地址6295620
c的地址6295624
d的地址6295624
e的地址6295626
f的地址6295628

十二 强制类型对齐

在源码的最上方写#pragma pack (value)
在这里插入图片描述

强制对齐规则:
1. 确定分配单位:min(value,默认分配单位)
2. 成员偏移量=min(value,成员自身类型字节数)  的整数(0-n)倍
3. 收尾工作=分配单位的整数(0-n)倍

例子:

[root@ansible9 ~]# cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma pack(2)
typedef struct
{
	char a;
	int b;
	short c;
} DATA1;

DATA1 data;
int main(int arg, char *argv[])
{
	printf("一共%d字节\n",(int)sizeof(DATA1));
	printf("a的地址%u\n",&data.a);
	printf("b的地址%u\n",&data.b);
	printf("c的地址%u\n",&data.c);
}
[root@ansible9 ~]# gcc test.c
[root@ansible9 ~]# ./a.out 
一共8字节
a的地址6295600
b的地址6295602
c的地址6295606

在这里插入图片描述

十三 位段

  1. 信息在计算机中存储长度一般以字节为单位;
  2. 有时不必用1字节,只需要1位或几位;

13.1 结构体中的位段

1. C语言在结构体中以位为单位来指定成员所占内存大小,以位为单位的成员称为位段或位域;
2. 一般用unsigned int或unsigned char两种类型做位段;
3. 排在前面的位段放内存低位;
4. 相邻位段可以压缩;
5. 段位不能取地址

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

13.2 无意义的位段(占位而不用)

在这里插入图片描述
在这里插入图片描述

13.3 位段的应用场景

在这里插入图片描述

13.4 另起一个位段

在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值