c++入门(下)

news2024/11/13 11:46:07

C++入门(下)

对于C++的基础语法的讲解,由想要实现多次重复的函数,引出宏函数和inline的内联函数的对比,对于inline的讲解和运用,在后,C语言中的NULL和C++中独特的nullptr的相比两者的比较,最重要的是对于引用的讲解和使用,以及和指针相比的区别。还有auto关键字的使用

文章目录

  • C++入门(下)
    • 宏函数
    • inline(内联函数)
    • NULL和nullptr
    • 引用
      • 引用和指针的不同点
    • auto关键字


宏函数

重复的调用同一个函数的时候,我们每创建一个函数,就会建立一个栈帧,这样对于空间来讲不友好,C语言中有宏函数这样的函数,来解决这一问题,下面是宏函数的特点与样例

优点:不需要建立栈帧,提高调用效率

缺点:复杂、容易出错,可读性差,不能调试

//宏函数实际上就是直接替换,宏函数书是不需要建立栈帧的,也就是可以直接展开的,所以宏函数的代码比较短,也相对复杂
/
#define Add(int x,int y) ((x)+(y))  //仅仅是x+y就需要这样书写,相对下面Add代码比较复杂
int Add(int x,int y){
    return x+y;//宏函数本身就是为了节约空间才选择的宏函数
}
int main()
{
    for(int i=0;i<100000;i++){
        Add(i,i+1);//重复多次调用同一函数,我们就直接定义了宏函数来解决
    }
    return 0;
}
//但是由于不能调试,等等缺点,我们C++中出现了内联函数 inline关键字

inline(内联函数)

inline(内联函数),顾名思义,这一个关键字,可以在使用函数的时候,不用创建栈帧,有着宏函数的优点,也补足了宏函数的缺点,可以调试,简单,不复杂,可读性强

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。

inline int Add(int x,int y){
    return x+y;
}
//inline的使用,只需要在Add函数之前加上关键字inline即可使得Add函数为内敛函数

inline看起来都是优点,都是好处,但对于接下来的代码进行分析, 你就明白inline的缺点了,或者是适用的地方

//加入说,我们在main函数中或者是一个程序中有10000个地方需要使用Add函数  Add函数的编译后得到的汇编代码比如是50行,那么对于下面函数分析,只看Add函数这一部分,在整个程序中所需的汇编代码有多少行
//内联函数inline
inline int Add(int x,int y){
    return x+y;
}
//如果是内联函数,由于我们不建立栈帧,也就是说,直接展开的方式使用Add函数,所以总汇编代码行数应该为:10000*50行

//使用正常的函数    
int Add(int x,int y){
    return x+y;
}
//在汇编代码中会调用这个函数 call  Add.... 这样的话汇编代码行数为  10000+50

所以在这个上面我们就发现了,inline,使得汇编代码变大,也就是导致最后可执行程序变大,所以inline并不是说适用于所有情况

inline内联函数适用情况

  • 适用于短小且频繁调用的函数

inline函数也并不是说,在函数头加上inline ,就一定会被编译器认定为内联函数

inline函数否决条件

  • 比较长的函数
  • 递归函数

结论:如果inline定义的函数,编译器会自行判定是否需要适用内敛函数,当函数体代码比较多,行数比较多的时候,编译器会默认为不能使用内敛函数,这也是为了使得最后生成的可执行程序大小没有那么夸张。

在默认为Debug版本下,inline不会起作用,为了方便调试

inline int Add(int x,int y){
    cout<<"111111"<<endl;
    cout<<"111111"<<endl;
    cout<<"111111"<<endl;
    cout<<"111111"<<endl;
    cout<<"111111"<<endl;
    cout<<"111111"<<endl;
    cout<<"111111"<<endl;
    cout<<"111111"<<endl;
    return x+y;//这种情况就不会判定为inline内联函数
}

总结

  1. inline是以空间换时间的做法,如果编译器将函数当成内敛函数,那就会在编译阶段,将函数体替换函数调用,缺点:可能会使得可执行文件变大(详情在上文),优点:减少调用开销,减少栈帧的创建,提高程序效率。
  2. inline只是一个对编译器的一个建议,向编译器发送一个请求,编译器可选择忽略这个请求
  • 函数较长
  • 递归函数
  1. inline适用的场景
  • 函数规模较小(至于多小,取决于编译器的规定,不同编译器规定不一样)
  • 频繁调用
  1. inline不建议声明和定义分离,放在两个文件的话,inline只是展开声明函数,就没有函数地址,所以找不到定义函数,链接就找不到这个函数

面试题

宏的优缺点?
优点:
1.增强代码的复用性。
2.提高性能。
缺点:
1.不方便调试宏。(因为预编译阶段进行了替换)
2.导致代码可读性差,可维护性差,容易误用。
3.没有类型安全的检查 。
C++有哪些技术替代宏?

  1. 常量定义 换用const enum
  2. 短小函数定义 换用内联函数 inline

NULL和nullptr

NULL和nullptr是表示不一样的意思,下面一个小程序就可以验证

NULL是宏定义 是0 或者是(void*)0,而nullptr是空地址(指针)

void ac(int) {
	cout << "int" << endl;
}
void ac(int*) {
	cout << "int*" << endl;
}
int main()
{	
	ac(0);
	ac(NULL);
	ac(nullptr);
	return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o7ca3v7L-1682251314257)(C:/Users/红颜/AppData/Roaming/Typora/typora-user-images/image-20230421105700926.png)]

引用

&在C语言中为取地址符,只有取地址的意思,在C++中不但有取地址的意思,也有另一个重要的含义:引用

引用的概念就是,一人多名,我可以叫你小王,也可以是why,这两个称呼的方式都是指的你,在程序中,表达的意思就是,我对你这个变量进行引用,得到一个称呼,这两个称呼都是指向你这个变量,类似指针,但是比指针更加方便理解

类型& 引用变量名(对象名) = 引用实体;

//类型& 引用变量名(对象名) = 引用实体; 这是引用的适用格式
int main()
{
    int a = 10;
    int& b = a;//给a一个引用,称为b,也就是说,ab都是指向同一个空间,数值为10,
    cout<<b<<" "<<a<<endl;//10 10
    b++;//引用类似于指针,当b数值变的时候,会直接影响到a
    cout<<b<<" "<<a<<endl;// 11 11
    return 0;
}

引用类型,需要和引用实体是同种类型,如:上述代码,即都需要int类型

在这里插入图片描述

引用特性

  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能引用其他实体
int main()
{
	int a = 10;
	int& b = a;
	int& c = a;
	int d = 20;
	c = d;//再次引用的时候,实际上不是引用,只是将d的数值赋值给c,进而改变了abc的数值都为d:20 
    //所以C还是a从别名
	cout << a << " " << b << " " << c << endl;
	return 0;
}

在这里插入图片描述

引用做返回值

当引用做返回值来接收的时候,我们对于这个情况做分析

int add(){
    static int n=0;
    n++;
    return n;
}
int main()
{
    int ac=add();//这样ac数值为1
    return 0;
}

在这里插入图片描述

如果不是static变量的话,会如何呢?

int add(){
    int n=0;
    n++;
    return n;
}
int main()
{
    int ac=add();//这样ac数值为1
    return 0;
}

在这里插入图片描述

传引用作为返回值

在函数类型的后面加上&,引用操作符,然后进行分析程序结果

int& add() {
	static int n = 0;//静态变量,存放在静态区,所以最后传出来的是静态区的n
	n++;
	return n;//所以返回的数值为1  返回给main函数的ans变量,过程为:用n的别名给了ans
}
int main()
{
	//rand();
	int ans = add();
	cout << ans << endl;//输出结果为1
	return 0;
}
//接下来对比一下没有static的临时变量

int& add() {
	int n = 0;
	n++;
	return n;
}//那么就是  ans得到的也是n的空间,当栈帧没有被破坏的时候输出的是1(正确)如果栈帧被破坏,就输出随机值
int main()
{
	//rand();
	int ans = add();//返回的也是n的别名,但是n的变量所在空间已经摧毁,所以n可以为1,或者为随机值
	cout << ans << endl;//输出的结果可能为1(栈帧没有被破坏),或者是随机值
	return 0;
}

当传引用且用int&来接受时

直接看代码,进行讲解,

int& add(int x) {
	int n = x;
	n++;
	return n;
}//那么就是  ans得到的也是n的空间,当栈帧没有被破坏的时候输出的是1(正确)如果栈帧被破坏,就输出随机值
int main()
{
	//rand();
	int& ans = add(10);
	cout << ans << endl;

	add(20);
	cout << ans << endl;
	return 0;
}

在这里插入图片描述

让我们再来测一测,是不是传引用,再用引用类型接收,两个变量代表两个相同的地址

int& add(int x) {
	int n = x;
	n++;
	cout << &n << endl;
	return n;
}//那么就是  ans得到的也是n的空间,当栈帧没有被破坏的时候输出的是1(正确)如果栈帧被破坏,就输出随机值
int main()
{
	//rand();
	int& ans = add(10);
	cout << &ans << endl;

	add(20);
	cout << &ans << endl;
	return 0;
}

在这里插入图片描述

可以用引用的地方:

  • 引用做参数(输出型参数)(减少拷贝,提高效率)
  • 引用做返回值(减少拷贝,提高效率)可以修改返回值(下面总结有相关代码)+获取返回值

权限可以缩小or平移,但是不能放大

我们知道的是int a的权限大于const a的权限,总结一点为引用过程中,权限可以平移或者进行缩小,但是不能进行放大,临时变量具有常性。

//正确用法
int main()
{
    int x=0;
    int& y=x;
    const int& z=y;//可以const 放小权限  这个是正确的用法
	//如果说我们改变y或者x的数值,z会不会随之变化呢?
    //如图1所示:
    y++;
    return 0;
}

//正确用例 (权限问题)
int main()
{
    double a=10.1;
    int b=a;
    const int& c=a;//输出结果为10.1 10 10 这是权限的缩小
    return 0;
}
//错误用例
int func()
{
	static int x = 0;
	return x;
}
int main()
{
	int& r = func();//初始化int不能赋值给int&类型(报错),实际上就是权限被放大,因为返回的是临时变量,临时变量返回是一个常量,所以应该是用const来接收
    //应该修正为
    const int& r=func();
	return 0;
}

图1 表明y改变时,在也会变化
在这里插入图片描述

在语法上,我们知道引用是不开空间的,使用指针是需要开空间的,但实际上在汇编底层指令实现的角度来讲,引用是类似于指针的方式来实现的,也就是说,引用语法上是不需要开空间的,但是实际上底层是会开空间

引用和指针的不同点

  1. 引用的概念的得到一个变量的别名,指针存储一个变量的地址
  2. 引用在定义时必须实现初始化,即不能出现int& a; 指针是没有这个要求的
  3. 引用初始化得到一个变量的别名后,在逻辑上不能再次引用其他变量,再次引用的时候,实际上是赋值,即将这个变量的数值赋值给这个引用变量。指针是可以随意指向任意变量的
  4. 没有NULL引用,但是又NULL指针
  5. sizeof关键字中的含义不同,引用sizeof得到的是引用类型的字节大小,指针sizeof大小是根据编译器决定的,如果是x86 32位那就是4字节,如果是64位就是字节
  6. 存在多级指针,但是没有多级引用
  7. 引用的自加将引用的实体加一,指针自加是将指针向后偏移一个类型的大小
  8. 访问变量的方式不同,指针是需要显示解引用,引用是编译器自己处理
  9. 引用比指针使用起来更加安全,没有NULL指针的现象

总结

  1. 基本上任何场景都可以使用引用传参

  2. 但是要谨慎使用引用传参,处理函数作用域的时候,对象如果不存在了,就不能用引用返回,还在就可以用引用返回。因为如果是临时变量作为引用返回值,可能会存在返回随机值的风险。

  3. 比如static修饰的变量作为引用返回可以,但是如果是普通的临时变量,不能这样返回,语法上可以,但是不建议这样使用。

//错误样例:
int& add(int x)
{
    int n=x;	//这是一个临时变量n,返回的时,可能会出现随机值这样的情况
    n++;
    return n;
}
//正确样例:
int& add(int x)
{
    static int n=x;	//这是一个静态变量n,返回时,是从静态区中进行引用返回
    n++;
    return n;
}
  1. 引用作为返回值,可以被赋值
int& add(int x) {
	static int n = x;
	return n;
}
int main() {
				
	int& ans = add(10);//ans得到的是n的别名,ans也是引用,我们紧接着调用add函数add(20),然后进行将50赋值给这个函数。
	add(20) = 50;
//为什么可以赋值?
    //实际上,add函数是引用传参返回,这样我们当然可以将数值再次赋值给这个函数
	cout << ans << endl;//ans的地址和add引用传参返回地址相同,输出数值为50
	return 0;
}

auto关键字

auto关键字的作用是,自动识别所需要的类型,也可以被用作增强for

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	int a = 10;
	auto c = a;
	//auto可以用作增强for循环使用,类似于Java中的Object
	//c++ 中的可以识别变量类型的关键字
	//typeid().name();
	int arr[] = { 1,241,54,152,12,12,1 };
	vector <int >v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	for (auto i : arr) {
	cout << i << endl;
	}
	auto d = &a;
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;
	return 0;
}

在这里插入图片描述

vector是STL里面的内容,现阶段不用管这个,这个地方只是展示一下auto增强for的用法,另外typeid(变量名).name()的用法,可以得到变量名的对应的类型

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

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

相关文章

kong(4):限流配置

Kong 提供了 Rate Limiting 插件&#xff0c;实现对请求的限流功能&#xff0c;避免过大的请求量过大&#xff0c;将后端服务打挂。 Rate Limiting 支持秒/分/小时/日/月/年多种时间维度的限流&#xff0c;并且可以组合使用。例如说&#xff1a;限制每秒最 多 100 次请求&…

Windows Server 2012R2 安装mysql 丢失VCRUNTIME140_1.dll------亲测

无去启动此程序,因为计算机中丢失VCRUNTIME140_1.dll。尝试重新安装该程序以解决此问题。 1.解决思路 说到底还是缺少底层的依赖&#xff0c;先下载依赖然后安装&#xff0c;最后安装vc。要不然vc是安装不成功。 下载安装--一下的插件 安装过程中需要重启一次电脑。 注意:必…

Android LoaderManager AsyncTaskLoader加载全部图片RecyclerView BigImageView呈现,Java(1)

Android LoaderManager AsyncTaskLoader加载全部图片RecyclerView BigImageView呈现&#xff0c;Java&#xff08;1&#xff09; 权限&#xff1a; <uses-permission android:name"android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:n…

VUE3子组件-业务代码优化

Vue3子组件 1.简介 Vue 3组件的主要优势之一就是它们可以帮助你将你的应用程序分解成可维护和可重用的部分。当你在应用程序中多次使用相同的代码时&#xff0c;你可以将它们抽象成一个组件&#xff0c;然后在应用程序中的多个地方使用该组件&#xff0c;而不必每次都编写相同…

5年了,终于入职阿里测试岗位,直接涨薪30K...

前言 本科毕业后就一直从事软件测试的工作&#xff0c;和多数人一样&#xff0c;最开始从事功能测试的工作&#xff0c;看着自己的同学一步一步往上走&#xff0c;自己还是在原地踏步&#xff0c;说实话这不是自己想要的状态。 一年半后开始沪漂生活&#xff0c;又摸爬滚打了…

阿里巴巴软件测试面试过了,起薪20k

普通二本计算机专业毕业&#xff0c;从毕业后&#xff0c;第一份接触测试的工作是在一家通讯小公司&#xff0c;大部分接触的工作是以功能测试为主&#xff0c;一直都是几千块钱工资&#xff0c;还一度被派出差&#xff0c;以及兼职各种产品、运维、运营的活&#xff0c;感觉自…

Cleer ARC II 音弧

戴上Cleer ARC II 音弧解放双耳&#xff0c;享受更自由的音符 用惯了各种入耳式耳机&#xff0c;换上开放式耳机&#xff0c;戴着确实更加舒服&#xff0c;特别是我现在用的这款Cleer ARC II 音弧&#xff0c;戴上还不容易掉&#xff0c;很适合运动使用。这款耳机采用一种耳挂佩…

JavaEE1(4/23)

目录 1.计算机CPU 2.CPU和GPU的区别 3.线程 4.内存是如何分配的 5.进程的调度 6.线程和进程的区别和联系&#xff1f; 1.计算机CPU 主频 &#xff1a;运算速度 3.73Ghz 表示每秒计算37.3亿次 基准速度&#xff1a;最小计算速度 睿频&#xff1a;最大运行速度 超频&…

【头歌C语言程序设计】结构体解答

写在前面 这道题总体来说还是偏难的&#xff0c;如果只看代码比较难以理解&#xff0c;当结构体的文章发出后&#xff0c;就有许多小伙伴问我这个问题&#xff0c;我开始意识到&#xff0c;可能我对这道题所作的解答还不够&#xff08;不装了&#x1f601;&#xff0c;根本没有…

Python进阶篇(三)-- TCP套接字与UDP套接字编程

1 Python3 网络编程 1.1 介绍 本文将首先利用 Python 实现面向TCP连接的套接字编程基础知识&#xff1a;如何创建套接字&#xff0c;将其绑定到特定的地址和端口&#xff0c;以及发送和接收数据包。其次还将学习 HTTP 协议格式的相关知识。在此基础上&#xff0c;本篇将用 Pyt…

Spring boot项目编译后未能加载静态资源文件

起因: 因甲方爸爸加了一个紧急的需求: 需要搞一个文件上传和下载功能. 我心中想:这不简单,搞个资源服务器,将上传文件放上去,然后访问资源链接就行了. 但接下来甲方爸爸说 不需要这莫麻烦,直接将文件放服务器里,用的时候下载到资源文件夹下就行. 我心里想: 我擦嘞 还能这样!! 然…

【C++】—— STL简介(了解)

前言&#xff1a; 在上节&#xff0c;我们简单的认识了模板的基本概念。今天我简单的给大家介绍一下关于 STL 。 目录 &#xff08;一&#xff09; 什么是STL &#xff08;二&#xff09; STL的版本 &#xff08;三&#xff09; STL的六大组件 &#xff08;四&#xff09;…

Tomcat概述以及部署与优化

一、Tomcat概述 1、Tomcat的概念 Tomcat是Java语言开发的&#xff0c;服务器是一个免费的开放源代码的Web应用服务器&#xff0c;属于轻量级应用服务器&#xff0c;在中小型系统和并发访问用户不是很多的场合下被普遍使用&#xff0c;是开发和调试JSP程序的首选。一般来说&am…

Python入门基础小练习

通过前面的两个篇章Python-入门基础篇和Python-入门基础语句篇大家应该已经认识了python基础的语句和函数了&#xff0c;并且可以使用pycharm编译器创建.py文件进行运行了&#xff0c;今天适当的来一些小练习&#xff0c;给枯燥的学习增添一些趣味性。 判断一个数是否为偶数 …

linux服务器实现百万并发遇到的问题以及解决思路

目录 前言服务器与客户端的配置介绍server 代码client 代码遇到的问题error too many open files为什么会出现这个问题解决这个问题的思路 killed&#xff08;已杀死&#xff09;为什么会出现这个问题解决这个问题的思路 最终结果学到的经验教训 前言 在完成百万并发服务器的时…

搭建家庭影音媒体中心 - 公网远程连接Jellyfin流媒体服务器

文章目录 前言1. 安装Home Assistant2. 配置Home Assistant3. 安装cpolar内网穿透3.1 windows系统3.2 Linux系统3.3 macOS系统 4. 映射Home Assistant端口5. 公网访问Home Assistant6. 固定公网地址6.1 保留一个固定二级子域名6.2 配置固定二级子域名 转载自远程穿透的文章&…

MySQL高级第十六篇:数据库日志有哪些(慢查询日志、通用查询日志、错误日志和二进制日志)

MySQL高级第十六篇&#xff1a;数据库日志有哪些&#xff08;慢查询日志、通用查询日志、错误日志和二进制日志&#xff09; 一、MySQL支持的日志有哪些&#xff1f;1. 日志类型总结2. 日志有什么缺点吗&#xff1f; 二、通用查询日志&#xff08;slow query log&#xff09;三…

【开发工具】idea2023.1社区版设置优化,媲美旗舰版

相信大家很多时候都是用旗舰版来开发&#xff0c;各种序列号破解包各种搞&#xff0c;但说不定哪天又失效了那天又爆泄漏隐私安全问题。随着idea的版本升级破解也不好搞了&#xff0c;所以我就直接用了社区版&#xff0c;经过一番折腾发现社区版一点不比旗舰版差&#xff0c;可…

Linux文件类型与属性

一、文件类型 Linux 系统下一共分为 7 种文件类型。通过 stat 命令或者 ls 命令来查看文件类型。 - &#xff1a;普通文件 d &#xff1a;目录文件 c &#xff1a;字符设备文件 b &#xff1a;块设备文件 l &#xff1a;符号链接文件 s &#xff1a;套接字文件 p &…

Jeston NANO 配置并安装 torch+ torchvision

由于nano的arm64架构&#xff0c;所以用它进行深度学习配置部署时会与用普通电脑&#xff08;x86&#xff09;有所不同: x86架构 x86架构是最常用的计算机架构之一&#xff0c;它是基于英特尔的8086处理器所设计的&#xff0c;后来又逐渐发展成x86-64架构&#xff0c;支持64位处…