• C++学习day--22 宏和结构体


    1、宏

    1.1 为什么要使用宏

    1、提高代码的可读性和可维护性
    2、避免函数调用,提高程序效率

     1.2 什么是宏

    它是一种预处理器指令,在预编译阶段将宏名替换为后面的替换体 。

    1.3 宏的定义

    由三部分组成:
    #define  WIDTH  960
    三个部分分别是:预处理指令  宏名 替换体( 多行可用 \ 延续 )
    知识点很简单,我们看一个代码:
    1. #include
    2. #include
    3. #define _width 1024 //宏命名规则同变量名
    4. #define ADDR "中华人民共和国湖南\
    5. 省平江县"
    6. int main(void) {
    7. printf("width: %d\n", _width);
    8. printf("我的祖籍: %s\n", ADDR);
    9. system("pause");
    10. return 0;
    11. }

    运行结果:

    注意:在定义宏的时候,如果要写宏不止一行,则在结尾加反斜线符号使得多行能连接上,如:#define HELLO "hello \

    the world"

     1.4 宏的使用

    1. 不带参数的宏
    2. 在宏中使用参数
    请注意一下的区别 :
    #define SQUARE(x)1   (x)*(x)
    #define SQUARE(x)2  x*x
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #define SQUARE1(x) (x)*(x)
    4. #define SQUARE2(x) x*x
    5. int main() {
    6. printf("%d\n", SQUARE1(1 + 2));
    7. printf("%d\n", SQUARE2(1 + 2));
    8. }

    运行结果:

    注意第一种宏它的替换是:(1+2)*(1+2)=9

    第二种宏它的替换是:1+2*1+2=5,替换后遵循四则运算,先算乘法再算加法。

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. //不带参数的宏
    4. #define _width 1024
    5. #define ADDR "中华人民共和国湖南\
    6. 省平江县"
    7. #define SQUARE(x) (x)*(x)
    8. #define MAX(x,y) x>y?x:y
    9. int main(void) {
    10. printf("width: %d\n", _width); // 宏展开相当于1024
    11. printf("width: %d\n", 1024);
    12. printf("我的祖籍: %s\n", ADDR);
    13. int i = 10;
    14. int j = SQUARE(i); // 宏展开 j = i*i;
    15. printf("j: %d\n", j);
    16. printf("MAX(i,j):%d\n", MAX(i, j)); //宏展开
    17. printf("MAX(i,j):%d\n", i > j ? i : j);
    18. int z = SQUARE(2 + 3); // (2+3)*(2+3) = 25
    19. printf("z: %d\n", z);
    20. system("pause");
    21. return 0;
    22. }

    运行结果:

    再看下面这个代码有什么问题:

    代码已经报错了,但是为什么,请注意,这里的王鹏程它并不是一个字符串,有人认为代码没错,打印的就是王鹏程啊,但是这里王鹏程不是字符串,他只是一个没有类型的量

    2、结构体

    2.1 为什么要使用“结构”(结构体)

    但需要表示一些复杂信息时,使用单纯的数据类型很不方便。 比如:学生信息(学号,姓名,班级,电话,年龄)

    2.2 什么是“结构”

    结构,就是程序员自定义的一种“数据类型” ,是使用多个基本数据类型、或者其他结构,组合而成的一种新的“数据类型”。

    2.3 结构的定义

    struct 结构名 {
       成员类型
       成员名;
       成员类型
       成员名;
    };

     注意结构体的定义,最后大括号外面那个分号不能少!

    实例:

    struct student {
            char name[16];
            int
            age;
            char tel[12];
    };
    特别注意:
    1)要以 struct 开头
    2)最后要使用分号
    3)各成员之间用分号隔开

     2.4 结构的初始化

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <string.h>
    4. //结构,就是程序员自定义的一种“数据类型”
    5. struct student {
    6. char name[16];
    7. int age;
    8. char tel[12];
    9. };
    10. //结构体包含结构体的情况,即嵌套
    11. struct _class {
    12. struct student rock;
    13. struct student martin;
    14. struct student zsf;
    15. };
    16. int main(void) {
    17. //结构体的初始化
    18. //方式一 定义的时候初始化所有的属性
    19. struct student rock = { "Rock", 38, "******" };
    20. printf("rock 的姓名: %s 年龄: %d 电话: %s\n", rock.name, rock.age,rock.tel);
    21. //方式二 定义的时候我们可以指定初始化的属性 VS/VC 不支持,但 gcc 是支持的
    22. //struct student s1 ={.name="张三丰",.age = 100};
    23. //方式三 单独初始化每一个属性
    24. struct student s2;
    25. strcpy(s2.name, "杨过");
    26. s2.age = 40;
    27. s2.tel[0] = '\0';
    28. printf("杨过的姓名: %s 年龄: %d 电话: %s\n", s2.name, s2.age,s2.tel);
    29. //结构体中含有结构体
    30. struct _class c1 = { {"Rock", 38, "******"},{"Martin", 38,"18684518289"},{"张三丰",100,""} };
    31. printf("c1 班 martin 同学的姓名: %s 年龄: %d 电话: %s\n",c1.martin.name, c1.martin.age, c1.martin.tel);
    32. system("pause");
    33. return 0;
    34. }

    注意:第二种初始化方式在VS编译器中不支持,因此不同编译器还是有差别的。

    运行结果:

     2.5 结构体的使用

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <string.h>
    4. struct student {
    5. char name[16];
    6. int age;
    7. };
    8. int main(void) {
    9. struct student s1, s2;
    10. printf("请输入第一个学生的姓名:\n");
    11. scanf_s("%s", s1.name, sizeof(s1.name));
    12. printf("请输入第一个学生的年龄:\n");
    13. scanf("%d", &s1.age);
    14. printf("第一个学生的姓名: %s, 年龄: %d\n", s1.name, s1.age);
    15. //结构体的小秘密,结构体变量之间可以直接赋值
    16. s2 = s1;
    17. printf("第二个学生的姓名: %s, 年龄: %d\n", s2.name, s2.age);
    18. //char c1[16] = { "martin" }, c2[16];
    19. //c2 = c1; //数组不能直接赋值
    20. system("pause");
    21. return 0;
    22. }

    运行结果:

    注意:结构体变量之间可以赋值,但是数组之间不能赋值,如代码最后两行,如果取消掉注释就会报错。数组之间不能直接赋值!! 

    结构体之间还可以通过memcpy( )函数赋值:

    1. #include
    2. #include
    3. #include
    4. struct student {
    5. char name[16];
    6. int age;
    7. };
    8. int main(void) {
    9. struct student s1, s2;
    10. printf("请输入第一个学生的姓名:\n");
    11. scanf_s("%s", s1.name, sizeof(s1.name));
    12. printf("请输入第一个学生的年龄:\n");
    13. scanf("%d", &s1.age);
    14. printf("第一个学生的姓名: %s, 年龄: %d\n", s1.name, s1.age);
    15. memcpy(&s2, &s1, sizeof(struct student));
    16. printf("第二个学生的姓名: %s, 年龄: %d\n", s2.name, s2.age);
    17. }

    运行结果:

     

    2.6 结构体数组

    结构体数组的定义:

    struct 结构名 变量名 [ 数组大小 ]

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <string.h>
    4. struct student {
    5. char name[16];
    6. int age;
    7. };
    8. int main(void) {
    9. struct student s[2];
    10. printf("请输入第一个学生的姓名:\n");
    11. scanf_s("%s", s[0].name, sizeof(s[0].name));
    12. printf("请输入第一个学生的年龄:\n");
    13. scanf("%d", &s[0].age);
    14. printf("第一个学生的姓名: %s, 年龄: %d\n", s[0].name, s[0].age);
    15. //结构体的小秘密,结构体变量之间可以直接赋值
    16. s[1] = s[0];
    17. memcpy(&s[1], &s[0], sizeof(struct student));
    18. printf("第二个学生的姓名: %s, 年龄: %d\n", s[1].name, s[1].age);
    19. system("pause");
    20. return 0;
    21. }

    运行结果:

    结构体数组的定义和普通数组定义完全一样。每个数组元素就相当于一个普通的变量。

    2.7 指向结构体的指针

    使用结构体变量址指针访问结构体成员要使用 -> 符号

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <string.h>
    4. struct _friend {
    5. char name[32];
    6. char sex; // m - 男 f -
    7. int age;
    8. };
    9. int main(void) {
    10. struct _friend girl = { "小龙女", 'f', 18 };
    11. struct _friend* my_girl = &girl;
    12. printf("小龙女的名字:%s, 性别:%s 年龄:%d\n", girl.name,girl.sex == 'm' ? "男" : "女", girl.age);
    13. //指针访问结构体变量的成员,有两种方式
    14. //方式 1. 直接解引
    15. printf("小龙女的名字:%s, 性别:%s 年龄:%d\n", (*my_girl).name,
    16. (*my_girl).sex == 'm' ? "男" : "女", (*my_girl).age);
    17. //方式 2. 直接使用指针访问 ->
    18. printf("小龙女的名字:%s, 性别:%s 年龄:%d\n", my_girl->name,my_girl->sex == 'm' ? "男" : "女", my_girl->age);
    19. system("pause");
    20. return 0;
    21. }

    运行结果:

     这块代码就是掌握结构体普通解引用和指针直接解引用的操作。实际开发中我们常用 -> 符通过指针去访问结构体指针变量的成员

     2.8 使用结构体传递值

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <string.h>
    4. struct programer {
    5. char name[32];
    6. int age;
    7. int salary;
    8. };
    9. //形参是结构体变量,值传递
    10. struct programer add_salary(struct programer p, int num) {
    11. p.salary += num;
    12. return p;
    13. }
    14. //形参使用结构体指针
    15. void add_salary1(struct programer* p, int num) {
    16. if (!p) return;
    17. p->salary += num;
    18. }
    19. //形参使用引用
    20. void add_salary2(struct programer& p, int num) {
    21. p.salary += num;
    22. }
    23. //形参是结构体变量,值传递,返回引用
    24. struct programer& add_salary3(struct programer p, int num) {
    25. p.salary += num;
    26. return p;
    27. }
    28. int main(void) {
    29. struct programer xiaoniu;
    30. strcpy(xiaoniu.name, "小牛");
    31. xiaoniu.age = 28;
    32. xiaoniu.salary = 20000;
    33. //结构体变量做为参数传值是值传递,和 int 等基本类型一样
    34. xiaoniu = add_salary(xiaoniu, 5000);
    35. printf("姓名: %s, 年龄: %d, 薪水: %d\n", xiaoniu.name, xiaoniu.age, xiaoniu.salary);
    36. //指针传值
    37. add_salary1(&xiaoniu, 5000);
    38. printf("姓名: %s, 年龄: %d, 薪水: %d\n", xiaoniu.name, xiaoniu.age, xiaoniu.salary);
    39. //引用传值
    40. add_salary2(xiaoniu, 10000);
    41. printf("姓名: %s, 年龄: %d, 薪水: %d\n", xiaoniu.name, xiaoniu.age, xiaoniu.salary);
    42. //返回引用
    43. xiaoniu = add_salary3(xiaoniu, 20000);
    44. printf("姓名: %s, 年龄: %d, 薪水: %d\n", xiaoniu.name, xiaoniu.age,xiaoniu.salary);
    45. }

    运行结果:

     

    当是值传递时,和普通数据类型一样,传递的是一份临时拷贝,对于结构体传递值,我们一般用指针和引用,因为我们知道,函数形参是在栈区,而栈区有限,结构体比较占用内存,因此值传递效率很低

    注意:
    一般不建议把结构体直接作为函数参数。 因为结构体的 size 比较大,直接传递,消耗性能!
    解决方案 : (使用指针和引用,优先使用引用)
  • 相关阅读:
    趣解设计模式之《新娘到底叫啥名啊?》
    C# SwinV2 Stable Diffusion 提示词反推 Onnx Demo
    Roson的Qt之旅 #115 QML Menu用法
    JavaScript学习笔记(一)
    零代码开发、可视化界面!飞桨AI Studio星河社区带你玩转Prompt应用
    左偏树学习笔记
    分享图片或链接到抖音
    Android音乐播放器(一)启动动画
    Java Maven Tomcat使用Tesseract-OCR文字识别(Tess4j)
    智慧医院智能导诊系统源码,智慧导诊小程序源码,采用前端框架:Uniapp+后端框架:springboot+移动端:微信小程序、H5自主研发
  • 原文地址:https://blog.csdn.net/qq_51956388/article/details/134084666