• [要素察觉]C语言七讲:结构体及C++引用讲解



    1. 结构体 - 结构体对齐 - 结构体数组

    1 结构体的定义、初始化、结构体数组

    • 有时候需要将不同类型的数据组合为一个整体,以便于引用。

    例如,一名学生有学号、姓名、性别、年龄、地址等属性,如果针对学生的学号、姓名、年龄等都单独定义一个变量,那么在有多名学生时,变量就难以分清。

    • C语言提供结构体来管理不同类型的数据组合。
    • 声明一个结构体类型的一般形式为:
    struct 结构体名
    	{成员列表}
    • 1
    • 2

    例如

    struct student
    	{
    		int num;char name[20];char sex;
    		int age;float score;char addr[30];
    	};
    
    • 1
    • 2
    • 3
    • 4
    • 5

    先声明结构体类型,再定义变量名。例如,

    struct student student1, student2;
    
    • 1
    • 结构体类型声明,注意最后一定要加分号。
    • 变量名和结构体名字不能一样。

    【例1.1】结构体的scanf读取和输出。

    #include 
    
    struct student
    {
    	int num;
    	char name[20];
    	char sex;
    	int age;
    	float score;
    	char addr[30];
    };//结构体类型声明,注意最后一定要加分号
    
    
    int main()
    {
    	struct student s = { 1001, "lele", 'M', 20,85.4, "shenzhen" };//定义及初始化
    	printf("%d %s %c %d %f %s\n", s.num, s.name, s.sex, s.age, s.score, s.addr);
    	//混合读取,%c不会忽略任何字符
    	scanf("%d%s %c%d%f%s",&s.num, s.name, &s.sex, &s.age, &s.score, s.addr);//数组名就是首地址
    	struct student sarr[3];
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    注意

    • 因为如果结构体变量已经定义,那么只能对他的每个成员单独赋值,如s.sum = 1003。

    • 结构体输出必须单独访问内部的每一个成员。
       
      printf不可以直接输出结构体,需要逐个成员访问,采用结构体变量名.成员名的形式来访问结构体成员。
      例如用s.num访问学号。

    • printf中的%类型要与各成员匹配。

    • 字符型数据(%c)不会忽略空格,如果要读取字符型数据,那么就要在待读取的字符数据和其他数据之间加入空格。

    【例1.2】结构体数组

    #include 
    
    struct student
    {
    	int num;
    	char name[20];
    	char sex;
    	int age;
    	float score;
    	char addr[30];
    };
    
    int main()
    {
    	struct student sarr[3];//定义结构体数组变量 - 一个数组中有三个结构体
    	int i;
    	for (i = 0; i < 3; i++)//结构体数组输入
    	{
    		scanf("%d%s %c%d%f%s", &s.num, s.name, &s.sex, &s.age, &s.score, s.addr);
    	}
    	for (i = 0; i < 3; i++)//结构体数组输出
    	{
    		printf("%d %s %c %d %5.2f %s\n", s.num, s.name, s.sex, s.age, s.score, s.addr);
    	}
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    2 结构体对齐

    结构体的大小必须是其最大成员的整数倍!

    • 结构体对齐是为了cpu高效读取内存上的数据。

    【例】结构体对齐

    #include 
    struct student_type1
    {
    	double score;//double是一种浮点类型,8个字节,浮点分为float和double,记住有这两种即可
    	short age; //2 - 8
    };//16
    
    struct student_type1
    {
    	double score;//8
    	int height;//4
    	short age;//2
    };//16
    //如果两个小成员(4+2)小于最大成员(8),可以结合在一起。
    //2+4 < 8,int和short占8个字节
    
    struct student_type1
    {
    	int height;//4
    	char sex;//1
    	short age;//2
    };//8
    //1+2 < 4,char和short占4个字节
    
    //结构体对齐 - 结构体占多大空间
    int main()
    {
    	struct student_type1 s1;
    	struct student_type2 s2;
    	struct student_type3 s3;
    
    	printf("s1 size=%d\n", sizeof(s1));
    	printf("s2 size=%d\n", sizeof(s2));
    	printf("s3 size=%d\n", sizeof(s3));
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    2. 结构体指针与typedef的使用

    1 结构体指针

    • 一个结构体变量的指针就是该变量所占据的内存段的起始地址。
    • 可以设置一个指针变量,用它指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。
    • 指针变量也可以用来指向结构体数组中的元素,从而能够通过结构体指针快速访问结构体内的每个成员。

    【例1.1】结构体指针的使用

    #include 
    
    struct student
    {
    	int num;
    	char name[20];
    	char sex;
    };
    
    int main()
    {
    	struct student s = { 1001,"wangle",'M' };
    	struct student sarr[3] = { 1001,"lilei",'M',1005,"zhangsan",'M',1007,"lili,'F" };
    	printf("%d\n",sizeof(s));//28
    	struct student *p;//定义结构体指针变量
    
    	int num;
    	p = &s;//拿到起始地址赋给结构体指针
    	printf("%d %s %c\n", p->num, p->name, p->sex);//对象指针->成员名
    	p = sarr;//结构体数组 - 数组名存的就是数组的起始地址 - 等价于p = &sarr[0]
    	//对象.成员名
    	printf("%d %s %c\n", (*p).num, (*p).name, (*p).sex);//方式一获取成员 - 加括号成为整体 - 取值*的优先级没有.的优先级高
    	printf("%d %s %c\n", p->num, p->name, p->sex);//方式二获取成员 - 这个方式更好
    	printf("-----------------------------------------\n");
    	p = p + 1;
    	printf("%d %s %c\n", p->num, p->name, p->sex);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • p就是一个结构体指针,可以对结构体s取地址并赋给p。借助成员选择操作符,就可以通过p访问结构体的每个成员,然后进行打印。
    • 数组名中存储的是数据的首地址,所以可以将sarr赋给p。
    • 使用(*p).num要加括号,因为“.”成员选择的优先级高于“*”(取值)运算符,所以必须加括号。
    • 对象.成员名
      对象指针->成员名
      方式二更好

    2 typedef的使用

    高效

    • 使用typedef声明新的类型名来代替已有的类型名。

    【例2.1】typedef的使用

    #include 
       
    //原写法
    //struct student 
    //{
    //	int num;
    //	char name[20];
    //	char sex;
    //};
    
    //typedef的使用 - 起别名
    // stu 等价于 struct student, pstu 等价于 struct student*
    //结构体指针
    typedef struct student
    {
    	int num;
    	char name[20];
    	char sex;
    }stu,*pstu; //别名 - 结构体/结构体指针
    
    typedef int INTEGER;//特定的地方使用 - 给int起别名
    
    int main()
    {
    	//struct student s = { 0 };//原写法
    	stu s = { 1001,"wangle",'M' };//别名
    	stu* p = &s;//定义了一个结构体指针变量
    	pstu p1 = &s;
    	INTEGER i = 10;
    	printf("i=%d,p->num=%d\n", i, p->num);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 使用stu定义结构体变量和使用struct student定义结构体变量是等价的。
    • 使用INTEGER定义变量i和使用int定义变量i是等价的。
    • pstu等价于struct student*,所以p是结构体指针变量。

    3. C++引用的讲解

    1 C++的引用讲解

    • 对于C++,新建源文件时,以.cpp后缀结尾。
    • C++的引用很便捷。

    例子:

    int a;
    void modifynum(int &b)
    {
    	b = b + 1;
    }
    //调用:modifynum(a)
    
    int *p = NULL;
    void modify_pointer(int *&p)
    {
    	......
    	p = q;
    	......
    }
    //调用:modify_pointer(p)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 在修改函数外某一变量时,使用了引用后,在子函数内的操作和函数外操作手法一致,效率更高。

    【例1.1】在子函数内修改主函数的普通变量(C++)

    #include //兼容c的头文件
    
    //在子函数中修改主函数中变量的值,使用引用;不需要修改,不用。
    void modify_num(int &b)//引用 - 形参中写&,要称为引用
    {
    	b = b + 1;
    }
    
    //C++的引用的讲解
    //在子函数内修改主函数的普通变量的值
    int main()
    {
    	int a = 10;
    	modify_num(a);
    	printf("after modify_num a = %d\n", a);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    将上面的代码改为纯C语言:

    【例1.2】在子函数内修改主函数的普通变量(纯C)

    #include 
    
    void modify_num(int *b)
    {
    	*b = *b + 1;
    }
    
    int main()
    {
    	int a = 10;
    	modify_num(&a);
    	printf("after modify_num a = %d\n", a);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 引用的效率更高
    • 在子函数中修改主函数中变量的值,使用引用;不需要修改,就不用。

    【例1.3】子函数内修改主函数的一级指针变量(重要

    #include 
    
    void modify_pointer(int *&p, int *q)//引用必须和变量紧邻
    {
    	p = q;
    }
    
    int main()
    {
    	int *p = NULL;//0
    	int i = 10;
    	int *q = &i;
    	modify_pointer(p, q);
    	printf("after modify_pointer *p = %d\n", *p);
    	return 0;//当进程已结束,退出代码不为0,那么代表进程异常结束
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 引用必须和变量紧邻。
    • 当进程已结束,退出代码不为0,那么代表进程异常结束。

    【例1.4】子函数内修改主函数的一级指针变量(纯C)

    #include 
    void modify_pointer(int **p, int *q)//相对于C++这里是int **p;
    {
    	*p = q;//这里的写法和例1.3中的是非常类似的
    }
    
    int main()
    {
    	int *p = NULL;
    	int i = 10;
    	int *q = &i;
    	modify_pointer(&p, q);//相对于C++这里是&p
    	printf("after modify_pointer *p = %d\n", *p);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    纯C使用了二级指针。

    2 C++的布尔类型

    • 布尔类型是C++的,有true和false。

    【例2.1】布尔类型也是有值的

    //设置布尔值的好处是提升了代码的可阅读性
    int main()
    {
    	bool a = true;
    	bool b = false;
    	printf("a = %d, b = %d\n", a, b);//ture = 1; false = 0
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    总结

    1.1

    • 结构体类型声明,注意最后一定要加分号。
    • 变量名和结构体名字不能一样。
    • 因为如果结构体变量已经定义,那么只能对他的每个成员单独赋值,如s.sum = 1003。
    • 结构体输出必须单独访问内部的每一个成员。
    • printf中的%类型要与各成员匹配。
    • 字符型数据(%c)不会忽略空格,如果要读取字符型数据,那么就要在待读取的字符数据和其他数据之间加入空格。

    1.2

    • 一个结构体变量的指针就是该变量所占据的内存段的起始地址。
    • 可以设置一个指针变量,用它指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。
    • 指针变量也可以用来指向结构体数组中的元素,从而能够通过结构体指针快速访问结构体内的每个成员。

    2.1

    • p就是一个结构体指针,可以对结构体s取地址并赋给p。借助成员选择操作符,就可以通过p访问结构体的每个成员,然后进行打印。
    • 数组名中存储的是数据的首地址,所以可以将sarr赋给p。
    • 使用(*p).num要加括号,因为“.”成员选择的优先级高于“*”(取值)运算符,所以必须加括号。

    2.2

    • 使用typedef声明新的类型名来代替已有的类型名。
    • 使用stu定义结构体变量和使用struct student定义结构体变量是等价的。
    • 使用INTEGER定义变量i和使用int定义变量i是等价的。
    • pstu等价于struct student*,所以p是结构体指针变量。

    3.1

    • 对于C++,新建源文件时,以.cpp后缀结尾。
    • C++的引用很便捷。
    • 在修改函数外某一变量时,使用了引用后,在子函数内的操作和函数外操作手法一致,效率更高。
    • 在子函数中修改主函数中变量的值,使用引用;不需要修改,就不用。
    • 引用必须和变量紧邻。
    • 当进程已结束,退出代码不为0,那么代表进程异常结束。

    3.2

    • 布尔类型是C++的,有true和false。
  • 相关阅读:
    数据隐私与链上交易如何双赢?首周 Web3 开发者集结精彩回顾
    Shell脚本函数简介及运用
    C++征途 --- 模板 -- 函数模板
    基于Spring Cloud创建弹性微服务
    8-8归并排序
    二分查找——经典题目合集
    Docker快速搭建漏洞靶场指南
    mysql返回值concat函数拼接,if函数,CASE WHEN函数条件判断。
    C#基础学习(二十三)_窗体与事件
    Revel框架基本使用实践教程
  • 原文地址:https://blog.csdn.net/m0_58991879/article/details/127779992