C与C++之结构体、共用体(四)

阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680

一、简述

对结构体、共用体的认识。
结构体是一种自定义的复合数据类型。

类比数组,数组也算一种复合数据类型,数组是由多个由相同数据类型的元素组成,(比如需要记录描述100个人的年龄,此时只需int arr[100]即可,不需一个一个的声明);结构体可由多中数据类型的元素组成(将不同的数据类型组合成为一个整体),比如描述一个学生信息,学生的学号使用long int类型存储、年龄用int类型存储、姓名用字符串(字符数组)存储。如果是一个学生,可以只需声明3个变量即可(long int id;int age;char* name[8]),要是100个学生,如此一个学生就得对应声明3个变量明显是不可行的;可以设想一下,能不能有一种数据类型是专门用来存储学生信息的?能,结构体就可以派上用场,结构体是一种自定义的数据类型,我们可以自己定义一种叫Student的数据类型,这种类型可以同时存放一个long int数据、int数据、字符串。如此,就可以声明Struct Student stu[100];来存放100个学生的数据了。

二、结构体的定义

结构体关键字:struct

定义student这种结构体(注意分号不能少),有三个成员变量id,age,name。

struct student
{
 
    long int id;
 
    int age;
 
    char name[8];
 
};

三、声明结构体变量

3.1普通方式 (注意声明变量时不能省略struct关键字,C++可以省略)

struct student stu1;    //定义了一个student类型的变量stu1

3.2 在定义的时候声明

struct student
{
 
    long int id;
 
    int age;
 
    char name[8];
 
}stu1;      //在结构体定义时,同时声明一个这种结构体变量stu1

四、结构体的初始化

4.1先声明结构体变量,后初始化。

struct student stu1;
 
stu1.id = 12345;            //通过成员运算符'.'来访问结构体的成员变量
 
stu1.age = 20;
 
strcpy(stu1.age,"Liang");     //因为数组在非初始化时,不能直接通过数组名直接赋值,strcpy函数需要包含头文件string.h    错误的写法:stu1.name = "Liang";

Windows环境下

 
19956127-eadc5d334ed989e7.png
 

Linux环境下

 
19956127-145e9095f9b560b4.png
 

4.2在声明结构体变量时同时初始化,类似于数组初始化。

#include <stdio.h>
#include <string.h>
 
struct student 
{
    long int id;
    int age;
    char name[8];
};
 
int main(int argc,char* argv[])
{
    struct student stu1 = {12345,22,"Liang"};
 
    printf("id=%ld age=%d name=%s 
",stu1.id,stu1.age,stu1.name);
    return 0;
}

Windows环境下

 
19956127-9800b7e7eea47880.png
 

Linux环境下

 
19956127-a549a866436d95b5.png
 

4.3在定义结构体类型的时候声明变量初始化。

Windows环境下

 
19956127-b136c157407c36fc.png
 

Linux环境下

 
19956127-2a5c4eb0dc720677.png
 

4.4 指定元素初始化

Windows环境下

 
19956127-5f63ccb668e93fe7.png
 
 
19956127-18348d8636a13432.png
 

Linux环境下

 
19956127-9626604b6dc4cf7d.png
 

五、结构体成员变量的访问

1、可使用成员运算符'.'来访问结构体成员变量。例如stu1.age = 10;int age = stu1.id;

2、可以使用结构体指针结合"->"来访问及饿哦固体成员变量。例如struct student *sp = &stu1;sp->id = 12345;

也可以 (sp).age = 20;注意成员运算符'.'的优先级高于指针操作符''。
Windows环境下

 
19956127-ce44675f41306918.png
 


Linux环境下

 
19956127-7934dd153fa70c30.png
 

六、结合使用typedef

使用typedef为自定义结构体定义别名,在声明变量时可以省略struct关键字。

typedef struct student 
{
    long int id;
    int age;
    char name[8];
}Student,*StuPtr;

也可以这样表示

typedef struct student 
{
    long int id;
    int age;
    char name[8];
}
 
typedef struct student Student;     //(这两句可合并为一句:typedef struct student Student,*StuPtr;) 
typedef struct student* StuPtr;   

Windows环境下

 
19956127-3e9329b1e07014a2.png
 

Linux环境下

 
19956127-1a9ed9599011ee44.png
 

七、结构体的大小

字节对齐以提高CPU效率。

比如我的机器是32位,CPU一次最大访问数据位数是32位(也就是4字节,那么默认4字节对齐)。

 
19956127-9a8c640354b7e102.png
 

测试代码:

struct ss
{
    char c;
    int a;
}s1={'a',0x12345678};
 
int main(int argc,char* argv[])
{
    
    printf("%d 
",sizeof(s1));
    return 0;
}

运行结果,(下断点调试)

 
19956127-3ca3839cf258b798.png
 

结构体会有内存对齐现象,所以所有结构体成员的大小之和并不一定是结构体的大小,结构体变量占据的内存单元的个数应当大于等于其内部所有数据成员占据内存单元数的和。
一般来说,不同的编译器字节对齐机制有所不同。字节对齐的细节和具体编译器实现相关,但一般而言,满足三个准则:

  1. 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
  2. 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;
  3. 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。

最宽基本类型的概念,所谓基本类型是指像char、short、int、float、double这样的内置数据类型。“数据宽度”就是指其sizeof的大小。诸如结构体、共用体和数组等都不是基本数据类型。
结构体、共用体结合使用时。

 
19956127-3467b447eea278de.png
 

八、共用体

共用体关键字union;
共用体也叫联合体,使几个不同类型的变量共占一段内存(相互覆盖),也就是说共用体的成员共用一片内存,后赋值的成员变量的数据才是共用体的生效数据,因为前面的赋值已经被覆盖了。共用体所占内存至少能够容纳最大的成员变量所需的空间,应用场景,比如需要一种既可以存储int型数据也可以存储double型数据的变量。比如识别设备,如果是U盘我要这样读取,如果是手机我又要这样读取……

共用体的大小

 
19956127-ead34a620fcfe2b8.png
 

共用体与结构体很多方面都很相似,比如定义、声明变量、初始化、赋值等等可以参考结构体部分。

8. 1共用体定义

union uu
{
    int i;
    double db;
};

8.2声明共用体变量 、初始化、赋值。(后赋值的成员变量会覆盖前面赋值的成员的数据)

 
19956127-54d7a2df6811e2cd.png
 

九、补充

1、同类型的结构体之间可以直接赋值

 
19956127-a980c5cd4f111e66.png
 


2、结构体嵌套

 
19956127-f4c7a47203957bf0.png
 


3、匿名结构体

 
19956127-9f20cc6ba7fa307d.png
 


匿名结构体只能再定义结构体的同时声明变量,定义之后无法再声明变量。应用场景:比如限定只有一个超级用户。

但是有个例外,使用typeof关键字来获取类型,然后可以声明这种变量。

 
19956127-1f8d28a60fa7a830.png
 


4、用结构体变量作参数----多值传递,因为函数有着副本机制,形参相当于实参的副本,当数据量很大时,效率较低。(数组最为函数参数时,会退化成为一个指针,效率高)
5、用结构体成员变量的地址求出结构体的首地址
测试代码:

#include <stdio.h>
 
#define GET_ENTRY(ptr,type,member)
           (type *)((char*)(ptr)-(unsigned long)(&(((type*)0)->member)));  
 
struct test
{
    char ch;
    int num;
    double db;
    char arr[22];
};
 
int main(int argc,char* argv[])
{
    struct test s1 = {'a',22,33.0,"hello world"};
    printf("addr=%p num=%d 
",&s1,s1.num); 
    
    double *dptr = &s1.db;
    struct test *sp = GET_ENTRY(dptr,struct test,db);
    //带参宏GET_ENTRY(dptr,struct test,db)展开--》(struct test *)((char*)(dp)-(unsigned long)(&(((struct test*)0)->db)));
    printf("addr=%p num=%d 
",sp,sp->num);
    return 0;
}

测试结果

 
19956127-d3a4557b31be175f.png
 


注释:

 
19956127-c73031c57eaca3e0.png
 

原文链接:https://blog.csdn.net/nanfeibuyi/article/details/81120804
阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680

原文地址:https://www.cnblogs.com/Android-Alvin/p/12109586.html