C++的一些基础语法

news2024/11/19 6:21:32

前言:

本篇将结束c++的一些基础的语法,方便在以后的博客中出现,后续的一些语法将在涉及到其它的内容需要用到的时候具体展开介绍;其次,我们需要知道c++是建立在c的基础上的,所以c的大部分语法都能用在c++上。

1.域作用限定符

首先,认识一下域作用限定符:

#include <iostream>

int main()
{
std::cout<<"hello world"<<std::endl;
return 0;

}

代码中域作用限定符就是::,当我们遇到在用一个项目组中使用了相同的命名的时候,除了改该怎么办?我们可以指定对应的域中去找相应的重复命名的内容,例如去某个结构体或者类中去找(类在后面会介绍);其次记住一点,如果域作用限定符的左侧为空,就是去全局里面去找。

2.关键字namespace

#include <iostream>
using namespace std;
int main()
{
  cout<<"hello world"<<endl;
  return 0;
}

首先可以明显的看到,函数里面的内容少了点什么,这就是因为我们加上了一句using namespace std;,为什么呢?

这里就开始介绍初学c++为什么要这样写程序了,因为大部分人学会使用了c++却连开始为什么要这样写都说不上一二,就有些尴尬了,首先介绍开始的头文件:

我们查阅文档可知(来源:https://legacy.cplusplus.com/reference/)iostream中包含了cin和cout等(其实它们都是iostream的对象),所以我们要想使用cout和cin等就要包含这个头文件。

那cout和cin等又是什么?我们目前记住它们是用来显示屏幕的信息和提取屏幕的信息的,分别需要搭配流插入<<和流提取>>,所以它们就都相当于c中的printf和scanf,但是不同的是它们不是库函数,而是某个类的对象,后面再提,以及与printf,scanf的区别也在后面再提。

然后endl只需要知道它是换行的意思即可。

然后就是我们的命名空间这一行,这一行是什么意思呢?首先我们需要知道

  • 用法:namespace name

{}

  • 命名空间---命名空间域,只影响使用,不影响生命周期,在多个文件使用相同的命名空间会被认为是同一个

然后我们需要知道std是c++标准库的名字,也就是说如果我们想用封装在c++标准库中的内容就需要展开这个命名空间,也就是展开标准库所在的域,这样才能使用库中的内容,如果不这样使用,cout等就会不被编译器所认识。

但是需要注意,你会在一些人的代码中发现是这样写的:

std::cout<<"hello world"<<std::endl

为什么呢?首先我们知道了命名空间的存在其实就是为了封装起你写的内容,避免其他的重复命名出现导致出问题,而如果要让标准库中的域展开,那就破坏了这道防线,因为其实封装的目的就是防止你的命名与标准库中的冲突,再使用与标准库中同名的就不行了,所以就有了这样的写法(平时练习可以直接展开,但比较大的项目中不建议这样用)。

如果我们将上方的写法称为全部展开,那下面的写法就为部分展开:

using std::cout;
using std::endl;

也就是让比较常用的展开,避免一个一个写。

3.流插入和流提取

刚刚我们已经提到了它们,例如cout<<endl就是让换行符流到控制台上,再看一个流提取的例子:

int n=0;
double* a=(double*)malloc(sizeof(double));
if(a==NULL)
{
  perror("malloc fail");
  exit(-1);
}
for(int i=0;i<n;++i)
{
  cin>>a[i];
}
for(int i=0;i<n;++i)
{
  cout<<a[i]<<endl;
}

 这是一个输入数组元素,然后打印出来的例子,从中我们可以看到cout和cin的用法,但是我们也可以发现我们在使用cout和cin的时候好像不用管a是一个double类型的数组唉?

这就是流的特性:

  • 流会自动识别类型,不需要%d%f等输入输出

至于为什么,我们将在流的内容与运算符重载中在提。

4.缺省(默认)参数与缺省值

我们知道,在c语言中,函数的参数是不能赋值的,也就是说你传来的什么就是什么,而c++有创造了一个新的玩法:

	void func(int a=0)
	{
	cout<<a<<endl;
	}
	int main()
	{
	func(1);//传参就是打印传的值,也就是1
	func();//不传就是打印默认值0
	return 0;
	}

至于有什么用,我们在以后再说(减少重载函数的数量,简化函数的调用过程)。

a.全缺省参数

全缺省就是所以参数都缺省:

void Func(int a = 10, int b = 20, int c = 30)
{
	cout << "a=" << a << endl;
	cout << "a=" << b << endl;
	cout << "a=" << c << endl;
	cout << endl;
}

int main()
{
	Func(1, 2, 3);
	return 0;
}

需要注意的是,只能从右往左缺省,且要连续,为什么呢?

因为如果你要写成:

void Func(int a = 10, int b = 20, int c )

那你传两个参数的时候,c对应的位置必须要有一个传过来的参数(因为没缺省嘛),那第一个实参是对应的a还是b,不仅编译器会报错,意义也不明;参数从右往左缺省就不会出现这样的问题。

然后就是使用缺省值要从右往左(因为参数是从右往左缺省的)连续使用,像这样不连续的就不行:

Func(,1,2);

b.半缺省参数

也就是参数可以不完全缺省,规则也和全缺省一样(从右往左连续缺省),不同的是不能不传参数,如果传一个参数就是对应最左边的参数
 

c.注意
  • 不能在声明和定义中同时给缺省参数,要在声明中给(如果声明和定义的缺省值不同,编译器无法确定,放在声明中也减少了其他源文件重复定义;还有些人说是为了保护代码的安全,因为别人copy了源码却不知道缺省值)
  • 缺省值必须是常量或者是全局变量,c语言不支持缺省参数

5.函数重载

在c中,我们知道使用函数名相同的函数是行不通的,所以本贾尼博士又发明了一个新的语法,允许函数名可以相同,但是参数的个数或者返回类型或者类型的顺序要不同的同名函数存在。

void Add(int a,int b);
void Add(int a,int b,int c);

void Add(int a,int b);
void Add(double a,double b);

void Add(int a,int b);
void Add(int b,int a);

那具体是怎么实现相同的函数名能找到对应的函数呢?

c++对函数名进行了修饰(此为linux的修饰规则,不同平台的修饰规则不同),例如void Add(int a,int b),会修饰成_Z3Addii,void Add(double a,double b)会修饰成_Z3Adddd,其中_Z是固定的,3是函数名长度,再加上函数名,再加上形参的类型缩写,通过不同的函数名修饰加上编译时产生的地址,就能区别出调用哪个了(浮点数默认为double)

 另外还需要注意:

  • 返回值不同的,其他都相同,不构成重载,因为编译器识别不出来,不是因为修饰名没有返回值而识别不出,而是调用函数的时候没写返回值(call这个函数的地址的时候没有返回值),区分不出来
  • 编译时就已经确定了调用那个函数 ,编译速度可能变慢,但是运行速度不会变慢

6.指针和引用

a.语法

可以看到k是i的引用,并且地址是一样的,所以我们又说k是i的别名。

同时我们还可以看到,直接赋值地址是不一样的,因为是又开辟了新的栈帧。

引用在后面的应用有很大的作用。

注意:

  •  引用在定义时必须初始化
  • 一个变量可以有多个引用
  • 引用一旦引用一个实体,再不能引用其他实体

b.应用
  • 应用1(输出型参数)---实参影响形参的情况,例如改变实参的值就要传实参的地址,从而形参要用指针
	void Swap(int& x,int& y)//形参是实参的别名,地址一样,不用取地址
	{
	  int tmp=x;
	  x=y;
	  y=tmp
    }
  • 应用2(做返回值)

首先,先引入传值返回:

我们可以看到:

返回的n先存到(拷贝给)一个临时变量(可能是寄存器也可能是上一层栈帧),然后count栈帧销毁,这个临时变量再给给ret,如果n是个静态的变量放在静态区,也是先存到临时变量,也不会直接返回n,此为传值返回(用于变量出了作用域就不在的情况)

如果使用传引用返回:

返回n的别名,减少了拷贝,因为n已经是静态的了,但返回时还要拷贝到一个临时变量,返回n的别名就减少了拷贝(可以理解产生了一个临时变量,但这个临时变量是n的别名,也就是n,就减少了拷贝 ),只要出了count作用域这个变量还在,就可以用传引用返回

所以使用传引用返回,我们可以得到以下结论:

  • 减少拷贝
  • 调用者可以直接修改返回的对象

我们再来看一个错误的例子:

int& Add(int a,int b)
{
  int c=a+b;
  return c; 
}

int main()
{
  int ret=Add(1,2);//正确的

  int& ret=Add(1,2);//错误的
}

返回c的别名,ret又是c这块空间的别名,但是c这块空间销毁了,访问ret也是找到销毁的空间,如果c的空间被覆盖就是随机值,没有就还是那块空间

总结:

如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回(性能上有一定的提升),如果已经还给系统了,则必须使用传值返回,以此理解全局变量二者都可以(因为全局变量也是存在静态区的,所以也是先拷贝给临时变量)。

c.常引用

	const int c=2;
	const int& d=c;
	
	const int* p1=NULL;
	const int* p2=p1;
	
	const int m=1;
	int n=m;
	
	int count{int n=1;return n;}
	const int& ret=count();
	
	int i=0;
    const double& rd=i;

从前两个例子我们可以知道d是c的别名,c是不可改的,那d也应该是不可改的;同理指针也是(*p不能修改,但是p可以修改,需要根据const的位置,在c专栏中有介绍到这一点),

赋值或初始化,权限可以缩小(只有别名是const修饰),但是不能放大。

而第三个例子就仅仅赋值,所以n的改变不会影响到m。

第四个例子是count的返回值是n先拷贝给临时变量,再给ret,返回的这个临时变量(在此过程中)也不可改变,它的别名也要用const修饰。

第五个例子是隐式类型转换,类型转换会产生临时变量,i先给给double类型的临时变量,临时变量再给给rd,所以rd是这个临时变量的别名,而这个临时变量(因为在这个过程里面这个临时变量肯定不变嘛)具有常性,所以要加const。

d.指针和引用的区别

首先我们需要知道:

  • 从语法的角度,引用不开空间,指针需要开空间;而从底层的角度,从汇编来看二者过程一模一样,引用也是需要开空间的

其次不同点:

这里的安全是从引用没有空引用个定义必须初始化出发的,因为有时指针的一些错误不会直接报错。

7.内联函数inline

a.复习宏

c++推荐使用enum和const代替宏常量,inline代替宏函数。

我们来复习一下宏的优缺点:

优点:

  • 类型可以不固定
  • 不用建立栈帧

缺点:

  • 不能调试

  • 没有类型安全的检查

  • 有些场景非常复杂(例如add宏函数)

#define ADD(x,y)((x)+(y))

1.为什么x+y要加括号,因为宏只是替换,例如ADD(1,2)*3;不加括号就达不到效果了

2.为什么里面的x和y分别要加括号?因为例如ADD(a|b,a&b);宏会替换成a|b+a&b,而加号  

   优先级更高,也达不到想要的效果

3.加分号也不行ADD(1,2)*3;宏会替换成((1)+(2));*3;会报错

b.使用
	inline int Add(int x,int y)
	{
      int z=x+y;
   	  return z;
	}

用汇编查看可知在调用内联函数时就展开了函数的逻辑,不用建立栈帧:

如果存在call Add就是没有展开函数,也就不是内联;红色箭头指向的就是没有call 这个函数的地址,直接展开函数体,使用了内联。 

c.内联函数的性质

关于第三点:
假设在头文件中声明一个内联函数,再在函数实现文件中定义一个内联函数,此时在主函数中使用内联函数就会链接报错,因为虽然主函数引用了头文件,但是内联函数是直接展开的,没有地址,头文件只定义了内联函数,但找不到内联函数的地址(也就是出现在了声明中,但是展开时找不到函数体),所以建议定义和声明都在头文件里。

注意:

如果成员函数在类中定义,编译器可能会将它当做内联函数处理。

所以在stl源码里面有些短小的函数直接就放在类里面定义了,就是当做了内联函数

有些大一点的函数就类中声明,类外定义,源码就这样搞的,所以声明定义分离还是有一定的道理的。

8.关键字auto

a.语法
int a=0;
auto b=a;
auto c=&a;

cout<<typeid(b).name<<endl;//int
cout<<typeid(c).name<<endl;//int*

实际价值是为了简化类型很长的代码。

b.缺点
  • 不能做形参
  • 不能声明数组
  • 定义变量必须初始化 

c.特性
  • auto后加*或者&就是限定类型为指针或者是引用 
  • auto a=1,b=2;
    auto c=1,b=2.0;//报错

    同一行定义多个变量时,这些变量必须是相同的类型,否则编译器会报错,因为编译器是根据第一个类型进行推导,再用推导出来的类型定义其他的。

d.typedef的缺点(附加)
typedef char* pstring;
int main()
{
  const pstring p1;//报错,需要初始化
  const pstring* p2;//正确
}

 这里所说的缺点就是如果是const char* p1,就不会报错,因为const修饰的是p1指向的空间,可以不对p1赋初始值,但是使用typedef就不行了,这里会被误解为const char p1,const修饰的是p1,要对p1赋初始值。

9.范围for(c++11)

	int array[]={0,1,2,3};
	for(auto e:array)//自动依次数组中的数据赋值给e对象,自动判断结束,所以e的改
	{
      cout<<e<<" ";     //变不会改变数组的值,想改数组值用auto& e
	}
	cout<<endl;

c++11提供的语法糖,底层其实是迭代器,知道怎么使用就行(分号前是范围内用于迭代的变量,右边是被迭代的范围) 

这里auto加上了引用是因为数据量大且涉及深拷贝(以后还会介绍)。

10.关键字nullptr

c++里NULL(NULL是一个宏)被定义为0,识别出来是int,实际c++不定义应该是((void*)0)

总结:

只介绍了c++中一些常见的基础语法,精进需要结合后面的内容,毕竟只学习语法却不了解应用是学不好的,本文如有错误请指正并会及时回复。

 

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

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

相关文章

【Docker】容器的概念

容器技术&#xff1a;容器技术是基于虚拟化技术的&#xff0c;它使应用程序从一个计算机环境快速可靠地转移到另一个计算机环境中&#xff0c;可以说是一个新型地虚拟化技术。 一、docker容器 Docker:是一个开源地容器引擎Docker 是一种轻量级的容器化技术&#xff0c;其主要原…

雷赛控制卡获取轴当前位置的值不正确问题处理

现像 从雷赛控制卡中获取当前轴位置值时发现轴在向零点的右边走时显示的值是负数。正常来就一般是要反馈正数的。一般轴零点右边是正方向&#xff0c;限位是正限位&#xff0c;反馈的位置也应该是正数。 如果雷赛软件中的【单轴参数】中的基本设置中的【脉冲模式】设置的是对的…

【神经网络与深度学习】LSTM(Long Short-Term Memory)神经网络模型

概述 LSTM&#xff08;Long Short-Term Memory&#xff09;是一种特殊的循环神经网络&#xff08;RNN&#xff09;结构&#xff0c;通常被用于处理和学习时间序列数据。因此&#xff0c;LSTM属于深度学习领域中的一种神经网络模型。 在深度学习中&#xff0c;LSTM被广泛应用于…

【轮式平衡机器人】——TMS320F28069片内外设之eCAP

引入 TMS320F28069的eCAP&#xff08;增强型捕获模块&#xff09;是一个强大的外设&#xff0c;用于精确测量和捕获输入信号的事件和时间戳。 在电机控制、传感器数据采集和信号处理等应用中&#xff0c;eCAP模块可以用于测量霍尔传感器、编码器或其他数字输入信号的周期、频…

MySQL 的基础操作

数据库的基础操作 1. 库操作2. 表的操作3. 数据类型 数据库是现代应用程序中至关重要的组成部分&#xff0c;通过数据库管理系统&#xff08;DBMS&#xff09;存储和管理数据。 1. 库操作 创建数据库 创建数据库是开始使用数据库的第一步。下面是一些常见的创建数据库的示例&a…

为什么在镀膜时要测薄膜折射率?

在芯片制造中&#xff0c;镀膜工序&#xff08;PVD,CVD&#xff09;是必不可少的关键环节&#xff0c;薄膜的质量直接影响了芯片的性能。对这些薄膜的精细控制又离不开对其折射率的深入理解和精确测量。今天将对芯片制造中薄膜折射率的概念、测量方法&#xff0c;以及它在整个制…

Unmanaged PowerShell

简介 在渗透测试当中经常会使用到PowerShell来执行脚本, 但是直接使用PowerShell.exe是一个非常敏感的行为, EDR等产品对PowerShell.exe进程的创建监控的很密切, 并且随着PowerShell的渗透测试工具的普及, 越来越多的EDR会利用微软提供的AMSI接口对PS脚本进行扫描, 但是对于低…

力扣最热100题——56.合并区间

吾日三省吾身 还记得梦想吗 正在努力实现它吗 可以坚持下去吗 目录 吾日三省吾身 力扣题号&#xff1a;56. 合并区间 - 力扣&#xff08;LeetCode&#xff09; 题目描述 Java解法一&#xff1a;排序然后原地操作 具体代码如下 Java解法二&#xff1a;new一个list&#xf…

Pytorch学习 day04(Totensor、Normalize、Resize、Compose)

Totensor 把一个PIL格式的图片&#xff0c;或者ndarray格式的图片转换为tensor格式使用方法&#xff0c;如下&#xff1a; from PIL import Image from torchvision import transforms from torch.utils.tensorboard import SummaryWriterimg Image.open("images/00130…

算法——滑动窗口之找到字符串中所有的字母异位词,串联所有单词的子串

6.找到字符串中所有的字母异位词 题目:. - 力扣&#xff08;LeetCode&#xff09; 6.1如何快速判断两个字符串是否是异位词 假设现在有s1 aabca,s2 abaca,那么这两个就是异位词,容易想到的判断方法就是将两个字符串按照字典序排序,再依次比较,但是时间复杂度很高;我们看看…

python并发编程:IO模型比较分析

blocking和non-blocking的区别在哪&#xff0c;synchronous IO和asynchronous IO的区别在哪。 blocking vs non-blocking&#xff1a;调用blocking IO会一直block住对应的进程直到操作完成&#xff0c;而non-blocking IO在kernel还准备数据的情况下会立刻返回。 synchronous …

【R语言爬虫实战】抓取省市级城市常务会议内容

&#x1f349;CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一&#xff5c;统计学&#xff5c;干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项&#xff0c;参与研究经费10w、40w级横向 文…

mysql如何开启手动提交事务

在mysql中&#xff0c;有一个变量autocommit&#xff0c;表示自动提交&#xff0c;默认为1&#xff0c;表示开启自动提交。通过以下命令查询 select autocommit;当autocommit为1时&#xff0c;任何一条sql语句都是一个事务&#xff0c;执行完由mysql自动提交。如果想自己决定什…

OS-Copilot:实现具有自我完善能力的通用计算机智能体

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ AI 缩小了人类间的知识和技术差距 论文标题&#xff1a;OS-Copilot: Towards Generalist Computer Agents with Self-Improvement 论文链接&#xff1a;https://arxiv.org/abs/2402.07456 项目主页&a…

32单片机基础:输入捕获测频率

接线图如下图所示&#xff1a; 我们复制之前写过的代码6-3 PWM驱动LED呼吸灯 在PWM模块中&#xff0c;执行的逻辑是&#xff0c;初始化TIM2的通道1&#xff0c;产生一个PWM波形&#xff0c;输出引脚是PA0&#xff0c;通过SetCompare1的函数&#xff0c;可以调节CCR1寄存器的值…

Memcached的重要性,如果防范Memcached DDOS攻击

一、Memcached简要 Memcached是一个开源的、高性能的、分布式内存对象缓存系统。它的主要目的是通过降低对数据库的访问来加速动态Web应用程序。 Memcached的用途非常广泛&#xff0c;它主要用于动态Web应用以减轻数据库负载。通过在内存中缓存数据和对象&#xff0c;Memcach…

pytorch_神经网络构建6

文章目录 强化学习概念实现qLearning基于这个思路,那么解决这个问题的代码如下 强化学习概念 强化学习有一个非常直观的表现&#xff0c;就是从出发点到目标之间存在着一个连续的状态转换&#xff0c;比如说从状态一到状态456&#xff0c;而每一个状态都有多种的行为&#xff…

2024/3/10 P1601高精加法

起因是本人在一个为数不多空闲的下午刷题ing&#xff0c;看到了AB problem。“就这&#xff1f;”从而忽略了高精这俩字&#xff0c;然后喜提40分。 于是开始研究何为高精。 #include<iostream> #include<string.h> #include<cstring>using namespace std; c…

数据结构与算法:链式二叉树

上一篇文章我们结束了二叉树的顺序存储&#xff0c;本届内容我们来到二叉树的链式存储&#xff01; 链式二叉树 1.链式二叉树的遍历1.1二叉树的前序&#xff0c;中序&#xff0c;后序遍历1.2 三种遍历方法代码实现 2. 获取相关个数2.1获取节点个数2.2获取叶节点个数2.3 获取树的…

代码随想录算法训练营第day14|二叉树理论基础、二叉树递归遍历、二叉树迭代遍历

目录 a.二叉树理论基础 b.二叉树递归遍历 c.二叉树迭代遍历 迭代法前序遍历 迭代法中序遍历 # 迭代法后序遍历 a.二叉树理论基础 在我们解题过程中二叉树有两种主要的形式&#xff1a;满二叉树和完全二叉树 满二叉树&#xff1a;如果一棵二叉树只有度为0的结点和度为2的…