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

news2024/12/23 13:56:56

1、宏

1.1 为什么要使用宏

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

 1.2 什么是宏

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

1.3 宏的定义

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

运行结果:

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

the world"

 1.4 宏的使用

1. 不带参数的宏
2. 在宏中使用参数
请注意一下的区别 :
#define SQUARE(x)1   (x)*(x)
#define SQUARE(x)2  x*x
#include <stdio.h>
#include <stdlib.h>

#define SQUARE1(x) (x)*(x)
#define SQUARE2(x) x*x

int main() {
	printf("%d\n", SQUARE1(1 + 2));
	printf("%d\n", SQUARE2(1 + 2));
}

运行结果:

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

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

#include <stdio.h>
#include <stdlib.h>

 //不带参数的宏
#define _width 1024
#define ADDR "中华人民共和国湖南\
省平江县"

#define SQUARE(x) (x)*(x)
#define MAX(x,y) x>y?x:y
int main(void) {
	printf("width: %d\n", _width); // 宏展开相当于1024
	printf("width: %d\n", 1024);
	printf("我的祖籍: %s\n", ADDR);
	int i = 10;
	int j = SQUARE(i); // 宏展开 j = i*i;
	printf("j: %d\n", j);
	printf("MAX(i,j):%d\n", MAX(i, j)); //宏展开
	printf("MAX(i,j):%d\n", i > j ? i : j);
	int z = SQUARE(2 + 3); // (2+3)*(2+3) = 25
	printf("z: %d\n", z);
	system("pause");
	return 0;
}

运行结果:

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

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

2、结构体

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

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

2.2 什么是“结构”

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

2.3 结构的定义

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

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

实例:

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

 2.4 结构的初始化

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

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

运行结果:

 2.5 结构体的使用

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student {
	char name[16];
	int age;
};
int main(void) {
	struct student s1, s2;
	printf("请输入第一个学生的姓名:\n");
	scanf_s("%s", s1.name, sizeof(s1.name));
	printf("请输入第一个学生的年龄:\n");
	scanf("%d", &s1.age);
	printf("第一个学生的姓名: %s, 年龄: %d\n", s1.name, s1.age);
	//结构体的小秘密,结构体变量之间可以直接赋值
	s2 = s1;
	printf("第二个学生的姓名: %s, 年龄: %d\n", s2.name, s2.age);
	//char c1[16] = { "martin" }, c2[16];
	//c2 = c1; //数组不能直接赋值
	system("pause");
	return 0;
}

运行结果:

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

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student {
	char name[16];
	int age;
};
int main(void) {
	struct student s1, s2;
	printf("请输入第一个学生的姓名:\n");
	scanf_s("%s", s1.name, sizeof(s1.name));
	printf("请输入第一个学生的年龄:\n");
	scanf("%d", &s1.age);
	printf("第一个学生的姓名: %s, 年龄: %d\n", s1.name, s1.age);
	memcpy(&s2, &s1, sizeof(struct student));
	printf("第二个学生的姓名: %s, 年龄: %d\n", s2.name, s2.age);
}

运行结果:

 

2.6 结构体数组

结构体数组的定义:

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

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

运行结果:

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

2.7 指向结构体的指针

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

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

运行结果:

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

 2.8 使用结构体传递值

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct programer {
	char name[32];
	int age;
	int salary;
};
//形参是结构体变量,值传递
struct programer add_salary(struct programer p, int num) {
	p.salary += num;
	return p;
}
//形参使用结构体指针
void add_salary1(struct programer* p, int num) {
	if (!p) return;
	p->salary += num;
}
//形参使用引用
void add_salary2(struct programer& p, int num) {
	p.salary += num;
}
//形参是结构体变量,值传递,返回引用
struct programer& add_salary3(struct programer p, int num) {
	p.salary += num;
	return p;
}
int main(void) {
	struct programer xiaoniu;
	strcpy(xiaoniu.name, "小牛");
	xiaoniu.age = 28;
	xiaoniu.salary = 20000;

	//结构体变量做为参数传值是值传递,和 int 等基本类型一样
	xiaoniu = add_salary(xiaoniu, 5000);
	printf("姓名: %s, 年龄: %d, 薪水: %d\n", xiaoniu.name, xiaoniu.age, xiaoniu.salary);

		//指针传值
	add_salary1(&xiaoniu, 5000);
	printf("姓名: %s, 年龄: %d, 薪水: %d\n", xiaoniu.name, xiaoniu.age, xiaoniu.salary);

		//引用传值
	add_salary2(xiaoniu, 10000);
	printf("姓名: %s, 年龄: %d, 薪水: %d\n", xiaoniu.name, xiaoniu.age, xiaoniu.salary);

		//返回引用
	xiaoniu = add_salary3(xiaoniu, 20000);
	printf("姓名: %s, 年龄: %d, 薪水: %d\n", xiaoniu.name, xiaoniu.age,xiaoniu.salary);
}

运行结果:

 

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

注意:
一般不建议把结构体直接作为函数参数。 因为结构体的 size 比较大,直接传递,消耗性能!
解决方案 : (使用指针和引用,优先使用引用)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1144664.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python 作用域:局部作用域、全局作用域和使用 global 关键字

变量只在创建它的区域内可用。这被称为作用域。 局部作用域 在函数内部创建的变量属于该函数的局部作用域&#xff0c;并且只能在该函数内部使用。 示例&#xff1a;在函数内部创建的变量在该函数内部可用&#xff1a; def myfunc():x 300print(x)myfunc()函数内部的函数 …

C++——类和对象(中)(2)尚未完结

拷贝构造 概念 在现实生活中&#xff0c;可能存在一个与你一样的自己&#xff0c;我们称其为双胞胎。 那在创建对象时&#xff0c;可否创建一个与已存在对象一某一样的新对象呢? 拷贝构造函数: 只有单个形参&#xff0c;该形参是对本类类型对象的引用(一般常用const修饰)&…

模块化编程

1、函数单独写在一个文件中.c&#xff0c;然后声明也写在一个文件中.h,在mian.c中引用 2、安装目录下面的文件夹用<>,在自己文件夹目录下就是"" 3、创建自己的&#xff08;先把函数放在c文件&#xff0c;再创建头h文件&#xff09;

ruoyi-plus创建模块、自动生成代码

ruoyi-plus自动生成代码 1、创建模块 复制其他部分的resouce过来 修改yml文件 2 修改Nacos 2.1 修改数据库文件 复制其他数据库的链接 &#xff0c;改为自己新建的数据库名字 修改为自己要生成的数据库 新建数据库的yaml文件 3 重启docker的ruoyi-gen服务 docker re…

数据结构【DS】B树

m阶B树的核心特性: Q&#xff1a;根节点的子树数范围是多少&#xff1f;关键字数的范围是多少&#xff1f; A&#xff1a;根节点的子树数∈[2, m],关键字数∈[1, m-1]。 Q&#xff1a;其他结点的子树数范围是多少&#xff1f;关键字数范围是多少&#xff1f; Q&#xff1a;对任…

SSD1306 oled显示屏的驱动SPI接口

有IIC接口 和SPI接口 还有8080,6080接口等 arduino SPI接口 直接使用u8g2库实现 //U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock*/ 13, /* data*/ 11, /* cs*/ 10, /* dc*/ 9, /* reset*/ 8); asrpro(SPI接口按下方修改&#xff0c;IIC接口官方有驱动&…

环形链表(C++解法)

题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&#…

Vmware下的虚拟机NAT连接后仍然木有网络

问题描述 出现在主机能ping通&#xff0c;互联网ping不通的情况。 废话 假设已经设置了网络配置文件IPADDR。 那么&#xff0c;NAT后可以访问互联网的前提是&#xff1a;这个IPADDR的网段在Vmware软件设置的网段内。 解决 在Vmware虚拟网络设置选项卡中&#xff0c;进NAT配…

10000字!图解机器学习特征工程

文章目录 引言特征工程1.特征类型1.1 结构化 vs 非结构化数据1.2 定量 vs 定性数据 2.数据清洗2.1 数据对齐2.2 缺失值处理 原文链接&#xff1a;https://www.showmeai.tech/article-detail/208 作者&#xff1a;showmeAI 引言 上图为大家熟悉的机器学习建模流程图&#xff0c;…

TYWZOJ 礼品配对包装 题解

文章目录 题目描述输入格式输出格式样例样例输入样例输出 数据范围与提示思路与部分实现完整代码 题目描述 爱与愁大神在这家目标店买了 2 x 2x 2x 份礼物&#xff0c;打算分给班级同学。其中有 x x x 份黑礼品&#xff0c; x x x 份白礼品&#xff0c; 2 x 2 2x2 2x2 个空…

计网小题题库整理第一轮(面向期末基础)(3)

基础选择题的最后一章更新&#xff0c;看完期末75至少没问题~ 前情提要&#xff1a; 计网小题题库整理第一轮&#xff08;12期&#xff09; 一.选择题 1、 目前,最流行的以太网组网的拓扑结构是&#xff08; C &#xff09;。 A&#xff09; 总线结构 B&#xff09; 环…

如何能够在发现问题和提问的时候一并带出自己的解决方案

1. 充分理解问题&#xff1a; 在提出问题之前&#xff0c;确保你已经完全理解了问题的本质。从不同的角度分析问题&#xff0c;确保没有遗漏任何重要的信息或者上下文。 2. 进行自我调查和研究&#xff1a; 在向他人寻求帮助之前&#xff0c;尝试自己解决问题。利用网络资源…

Go学习第十三章——Gin(入门与路由)

Go web框架——Gin&#xff08;入门与路由&#xff09; 1 Gin框架介绍1.1 基础介绍1.2 安装Gin1.3 快速使用 2 路由2.1 基本路由GET请求POST请求 2.2 路由参数2.3 路由分组基本分组带中间件的分组 2.4 重定向 1 Gin框架介绍 github链接&#xff1a;https://github.com/gin-gon…

从零开始搭建Prometheus+grafana服务器组件监控系统

服务器及相关组件监控 本文档主要记录了常用企业级服务器及各种组件的监控手段和监控部署方案&#xff0c;使企业可以实时感知服务器组件的健康状态&#xff0c;并在服务器或组件出现异常时及时做出反应。 本方案采用的Prometheusgrafana的方式实现对服务器及各种组件的监控&am…

【前端】NodeJS核心知识点整理

1.Node.js入门案例 1.1.什么是Node.js JS是脚本语言&#xff0c;脚本语言都需要一个解析器才能运行。对于写在HTML页面里的JS&#xff0c;浏览器充当了解析器的角色。而对于需要独立运行的JS&#xff0c;NodeJS就是一个解析器。 每一种解析器都是一个运行环境&#xff0c;不但…

C/C++数据结构之深入了解线性表:顺序表、单链表、循环链表和双向链表

线性表是一种基本的数据结构&#xff0c;它在计算机科学中起着至关重要的作用。线性表用于存储一系列具有相同数据类型的元素&#xff0c;这些元素之间存在顺序关系。在C/C中&#xff0c;我们可以使用各种方式来实现线性表&#xff0c;其中包括顺序表、单链表、循环链表和双向链…

基于白鲸优化算法BWO优化的VMD-KELM光伏发电短期功率预测MATLAB代码(含详细算法介绍)

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; VMD适用于处理非线性和非平稳信号&#xff0c;例如振动信号、生物信号、地震信号、图像信号等。它在信号处理、振动分析、图像处理等领域有广泛的应用&#xff0c;特别是在提取信号中的隐含信息和去除噪声方面…

无头浏览器自动化:Puppeteer 帮你释放效能 | 开源日报 No.64

facebook/react Stars: 209.5k License: MIT React是一个用于构建用户界面的JavaScript库。它具有以下优势和特点&#xff1a; 声明式&#xff1a;React使得创建交互式UI变得轻松。您可以为应用程序中的每个状态设计简单视图&#xff0c;当数据发生更改时&#xff0c;React会…

Postman日常操作

一.Postman介绍 1.1第一个简单的demo 路特斯&#xff08;英国汽车品牌&#xff09;_百度百科 (baidu.com) 1.2 cookie 用postman测试需要登录权限的接口时&#xff0c;会被拦截&#xff0c;解决办法就是每次请求接口前&#xff0c;先执行登录&#xff0c;然后记住cookie或者to…

ChineseChess5 2023.10.28

中国象棋残局&#xff1a;黑双卒单车压境解棋