【C++初阶】小白入门C++

news2024/12/24 10:15:12

目录

  • 前言:
  • 1、C++关键字
  • 2、命名空间
    • 2.1命名空间是什么
    • 2.2为什么要有命名空间
    • 2.3命名空间怎么使用
      • 2.3.1命名空间的写法
      • 2.3.2命名空间是可以嵌套的
      • 2.3.3使用命名空间的三种方式
  • 3、C++输入和输出
    • 3.1初识cout和cin
    • 3.2C++的输入输出可以自动识别变量类型
  • 4、缺省参数
    • 4.1缺省参数是什么
    • 4.2缺省参数的分类和使用
  • 5、函数重载
    • 5.1函数重载的定义和使用
    • 5.2重载二义性区分
    • 5.3为什么C++支持重载,而C不能
  • 6、引用
    • 6.1什么是引用
    • 6.2为什么有引用
    • 6.3引用的使用规范
      • 6.3.1引用的写法
      • 6.3.2引用必须初始化
      • 6.3.3引用不能改变指向
      • 6.3.4一个对象可有多个别名
    • 6.4引用的如何使用
      • 6.4.1引用做参数
      • 6.4.2引用做返回值
      • 6.4.3常引用
      • 6.4.4常引用和类型转换
    • 6.5引用与指针
      • 6.5.1引用和指针的异同
      • 6.5.2不同点
  • 7.内联函数
    • 7.1什么是内联函数
    • 7.2为什么要有内联函数
    • 7.3内联函数的特性
      • 7.3.1提高程序运行效率
      • 7.3.2inline 只是一个建议
      • 7.3.3内联不能声明和调用分离
  • 8.auto关键字
    • 8.1介绍auto
    • 8.2为什么有auto
    • 8.3 auto的使用规则
  • 9.指针空值nullptr(C++11)

前言:

从本篇文章开始就正式进入C++的学习了,C++在C语言的基础上增加了面向对象的思想,还有许多库,在使用上相比C语言大大提高了效率,同时在语法上也填上C语言的坑。本篇文章是介绍C++入门的基础语法知识,接下来我们一起往下看吧~
在这里插入图片描述

1、C++关键字

C++总共有63个关键字,C语言有32个。毕竟是从C语言 “进化” 出来的,补充的东西肯定比较多。有些关键字我们是已经知道的,还有一小部分本文后面会有介绍,其他的以后会重点讲解的,这里只是看看C++有哪些关键字。
在这里插入图片描述

2、命名空间

2.1命名空间是什么

命名空间是C++标准库中一个概念,顾名思义,简单来说就是定义某个变量或者函数的名称放在指定的空间里,这个空间也有它的名字,编程者要可以通过命名空间找到要使用的变量或者函数。例如 std 就是C++标准库定义的命名空间名,要使用cout、cin等这些函数不仅要有对应的头文件,还要对命名空间进行展开(这个后面也会具体讲)才能使用,因为C++将标准库的定义实现都放在这个命名空间里。

2.2为什么要有命名空间

我们之前学习C语言时会发现一个问题,或者说这是C语言的语法规则。C语言语法规则:在全局作用域中,两个变量不能有相同的名称,函数也一样。

可是如果在某些场景下必须用到相同的名称怎么办,不得已修改变量名或者是函数名容易造成混乱。

所以,C++补充了命名空间这个新语法,可以支持在全局作用域中出现两个甚至多个名称相同的变量或者函数。只要在命名空间中定义某个变量,另一个变量是全局变量(或者是在另一个命名空间),我们只要在去相应的命名空间里找那个变量就可以使用,两个名称相同也不会有命名冲突。

举个栗子:小明的爷爷在山上种了一片果林,小明去山上摘果子,但是好巧不巧,山上还有另一片果林,于是小明懵了,不知道哪个果林是爷爷种的,万一摘错了岂不是自找麻烦,小明只好下山。后来,爷爷在自己的果林建了围墙,并且门口标注着爷爷的名字,小明再次上山就找到了爷爷的果林,可以进入摘果子了。

看下面这段代码:

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

int rand = 10;
int main()
{
	printf("%d\n", rand);
	return 0;
}

我们定义了一个全局变量rand,可是有#include <stdlib.h>这个头文件,在这个头文件里rand是一个函数名,也就是说我们定义的这个全局变量的名称与函数的名称重了,所以编译器报错。
在这里插入图片描述

2.3命名空间怎么使用

2.3.1命名空间的写法

定义一个命名空间,需要命名空间关键字——namespace,然后在命名空间后面就是这个命名空间的名字,名字是自定义的,一般习惯写成自己姓名的首字母。命名空间里可以定义变量、函数、类型。

// 关键字+命名空间名
namespace yss
{
	int Add(int a, int b)//定义函数
	{
		int c = a + b;
		return c;
	}
}

定义完这个函数,我们在main函数里要使用这个函数,还要了解一个符号

域作用限定符—— ::

域作用限定符由两个冒号组成,可以通过域作用限定符访问命名空间里的函数。

//   命名空间名 + 域作用限定符 + 函数名
int ret = yss::Add(1, 3);

注意:只要定义的不管是变量、函数还是类型在命名空间里,都要通过这种方式访问,否则找不到该变量/函数/类型

2.3.2命名空间是可以嵌套的

namespace yss
{
	namespace yss1
	{
		int Add1(int a, int b)
		{
			int c = a + b;
			return c;
		}
	}
	int Add(int a, int b)
	{
		int c = a + b;
		return c;
	}
}

int main()
{
	int ret = yss::Add(1, 3);
	int ret1 = yss::yss1::Add1(2, 4);
	return 0;
}

要找到Add1这个函数先要进入yss这个命名空间,然后进入yss1这个命名空间。

注意:如果多个文件中有相同的命名空间,也就是这些命名空间的名字相同,那么编译器就会把它们整合成一个整体;同理,在一个文件中有多个相同的命名空间也是这样的。

2.3.3使用命名空间的三种方式

第一种:全部展开

namespace yss
{
	int Add(int a, int b)
	{
		int c = a + b;
		return c;
	}
}
using namespace yss;//
int main()
{
	int ret = Add(1, 3);
	printf("%d %d", ret);
	return 0;
}

用这种方法就可以不加域作用限定符。

第二种:局部展开

namespace yss
{
	int Add(int a, int b)
	{
		int c = a + b;
		return c;
	}
}
using yss::Add;
int main()
{
	int ret = Add(1, 3);
	printf("%d %d", ret);
	return 0;
}

第三种:带上命名空间名 + 域作用限定符(前面的就是)

3、C++输入和输出

3.1初识cout和cin

刚学习C语言,我们输出的第一个程序是Hello World! ;初学C++,我们也要知道它的输出函数,以及使用它打印出Hello World!

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

在这里插入图片描述
我们先不管cout、endl以及<<是什么,再来第一个C++程序的输入并输出:

#include <iostream>
using namespace std;
int main()
{
	int a = 0;
	cin >> a;
	cout << a;
	return 0;
}

在这里插入图片描述
看完C++的输入和输出,我们发现这可比C语言的输入和输出好写多了。但是,我们要了解下C++的输入和输出。

cout是标准输出对象;cin是标准输入对象;endl是特殊的C++符号,表示换行。它们都包含在 < iostream > 这个头文件里,并且按照命名空间的使用方法使用std

<<是流插入运算符;>>是流提取运算符

3.2C++的输入输出可以自动识别变量类型

#include <iostream>
using namespace std;
int main()
{
	float f = 0;
	cin >> f;
	cout << f << endl;
	int i = 0;
	cin >> i;
	cout << i << endl;
	return 0;
}

在这里插入图片描述
那么又有一个问题,假如要保留一位小数怎么办?其实很简单,因为C++兼容C,所以C里面的东西C++也能用。

控制小数点:

printf("%.1f", f);

补充:
std是C++标准库的命名空间,使用using namespace std展开标准库就会全部暴露,如果定义的变量/函数/类型与库里面的重名就会有冲突,这种问题一般在例如项目开发这些代码较多,规模较大的情况下会发生,所以在项目开发中,一般都采用局部展开。我们在日常练习中很少使用那些库里面的函数,代码也不是很多,为了方便操作和练习,所以在日常练习中可以采用全部展开。

4、缺省参数

4.1缺省参数是什么

缺省参数是在声明或者定义函数时为函数的参数指定一个或者多个缺省值。调用这个函数,如果实参没有值传过去,就由形参的缺省值来充当参数,否则使用指定的实参。C语言不支持缺省参数。

比如,我们正常调用一个函数是这样的:

void Func(int a, int b)
{
	cout << a + b << endl;
}
int main()
{
	Func(1, 2);
	return 0;
}

形参的部分是类型+变量名

调用带有缺省参数的函数:

void Func(int a = 10, int b = 20)
{
	cout << a + b << endl;
}
int main()
{
	Func();
	return 0;
}

在这里插入图片描述
当没有传参时,使用参数的默认值,即缺省参数。

注意:缺省值必须是常量或者全局变量

4.2缺省参数的分类和使用

全缺省参数:

void Func(int a = 1, int b = 2, int c = 3)
{
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;
}
int main()
{
	Func();
	return 0;
}

在这里插入图片描述
半缺省参数:

void Func(int a, int b = 2, int c = 3)
{
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;
}
int main()
{
	Func(10);
	Func(10, 20);
	Func(10, 20, 30);
	return 0;
}

在这里插入图片描述

半缺省参数必须从右往左依次来给出,不能间隔着给

int a, int b = 2, int c
int a = 1, int b = 2, int c

以上这些是错误的!

缺省参数不能在函数声明和定义中同时出现
因为当两个同时出现缺省参数时,如果两边的缺省值相同那倒还没什么,如果不相同调用这个函数不知道使用哪边的缺省值,产生歧义。所以,函数声明和定义分离的情况下,在声明时给缺省参数较好。

5、函数重载

5.1函数重载的定义和使用

在C++中,允许同一作用域中出现名字相同但类型/参数个数/类型顺序不同的函数,这类函数叫做重载函数。常用来解决功能类似数据类型不同的问题。

类型不同:

void Add(int a, int b)
{
	cout << a + b << endl;
}
void Add(double a, double b)
{
	cout << a + b << endl;
}
int main()
{
	Add(1, 3);
	Add(1.1, 3.3);
	return 0;
}

在这里插入图片描述
参数个数不同:

void Add(int a, int b)
{
	cout << a <<endl << b << endl;
}
void Add(int a)
{
	cout << a << endl;
}
int main()
{
	Add(1, 3);
	Add(6);
	return 0;
}

在这里插入图片描述

类型顺序不同:

void Func(int a, char c)
{
	cout << a << endl;
	cout << c << endl;
}
void Func(char a, int c)
{
	cout << a << endl;
	cout << c << endl;
}
int main()
{
	Func(1, 'y');
	Func('u', 2);
	return 0;
}

在这里插入图片描述

5.2重载二义性区分

一:函数参数类型不同

void Add(int a, int b)
{
	cout << a + b << endl;
}
void Add(double a, double b)
{
	cout << a + b << endl;
}
int main()
{
	Add(1, 3);
	Add(1.1, 3.3);
	Add(1, 3.3);///
	return 0;
}

第三个Add函数一个参数的类型是整型,另一个是浮点型,那么这时这个Add函数不知道应该调用上面两个中的哪一个。如果是第一个,就是double转int ,如果是第二个,就是int转double,所以这里产生了歧义,编译器会报错。
在这里插入图片描述
二:无传参时形参是缺省参数
先来看一个正常运行的代码:

void Func()
{
	cout << "func" << endl;
}
void Func(int a)
{
	cout << "func" << endl;
}
int main()
{
	Func();
	Func(1);
	return 0;
}

无传参的调用上面没有形参的函数,有传参的调用上面有形参的函数。接下来让形参 a = 10

void Func()
{
	cout << "func" << endl;
}
void Func(int a = 10)
{
	cout << "func" << endl;
}
int main()
{
	Func();
	Func(1);
	return 0;
}

我们可以发现这样的错误:
在这里插入图片描述
因为当调用没有传参的函数时,既可以是对应最上面的函数,也可以是下面那个函数,前面的缺省参数学习过,当没有传参时,默认使用缺省值,所以产生了歧义。

三:函数名、参数类型都相同,返回值不同
刚学习重载函数时,我们知道重载函数的函数名相同,类型不同,或者参数个数不同。但是为什么不能返回值不同呢?
看下面代码:

int Func()
{
	return 2;
}
double Func()
{
	return 2.3;
}int main()
{
	Func();
	return 0;
}

错误如下:
在这里插入图片描述
我们调用一个函数,但是不知道是上面的哪个函数返回,所以返回值不同不能构成重载。

5.3为什么C++支持重载,而C不能

要了解C++支持重载的原因,我们得先了解程序运行起来的流程

一个项目的规模往往是比较大的,所以不可能所有代码都挤在一个文件里写,分文件写代码是必需的。那么,当程序运行起来,大致需要经历这4个步骤:

预处理——>编译——>汇编——>链接

假设 a.cpp 里调用 b.cpp中定义的函数,编译后还未链接,a.o 的目标文件没有函数的地址,因为函数是在 b.cpp 中定义的,所以函数的地址在 b.o 里。接下来的链接就起作用了,a.o调用函数却没有函数的地址,链接器就会去 b.o 的符号表中找函数的地址,然后链接起来。

而这里链接器又会怎么找呢?答案是通过函数名字去找,但是不同的编译器找法不同,也就是函数名修饰规则不同。

这里我们通过Linux分别采用gcc和g++来看下(Windows比较复杂)
gcc编译器:
在这里插入图片描述
g++编译器:
在这里插入图片描述
可以看出,Linux环境下,gcc编译器完成后函数名字没有发生改变;而g++编译器完成后修饰过的函数名是【_Z+函数长度+函数名+类型首字母】。这就可以解释为什么C语言不支持重载,因为修饰过后的函数名依然是不变的,没办法区分开来;而C++编译器通过函数名修饰规则只要函数参数个数不同或者参数类型不同就能够区分开,也就支持重载了。

6、引用

6.1什么是引用

引用不是新增加的一个变量,而是给已经存在的变量起的别名。引用与引用的对象共用一块内存空间。

例如,铁牛是李逵的别名,铁牛和李逵是同一个人。

int main()
{
	int a = 1;
	int& b = a;
	return 0;
}

a是已经存在的变量,b是a的引用或者说是别名,b不单独占一块空间,而是与a共用一块内存空间,所以改变b就是改变a。

6.2为什么有引用

我们知道,C语言中的指针操作是比较危险的,如果使用不当,会有非常严重的后果。所以C++新增了引用这个概念,它可以修改引用的对象,而且也使程序的可读性较好,安全性更高。

6.3引用的使用规范

6.3.1引用的写法

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

	int a = 3;
	int& b = a;

注意:引用类型必须和引用实体是同种类型的

6.3.2引用必须初始化

引用必须有所引用的对象,即初始化。

int main()
{
	int a = 3;
	int& b;
	return 0;
}

没有初始化是错误的,可看编译器显示:
在这里插入图片描述

6.3.3引用不能改变指向

以前我们学习过链表,链表中指针改变指向的内容来实现链表的增删查改,但是,C++规定,引用不能改变指向。
例如:

int main()
{
	int a = 2;
	int b = 3;
	int& c = a;
	int& c = b;
	return 0;
}

在这里插入图片描述

c是a的别名,又是b的别名,如果对c进行修改,那么到底是改a还是改b呢?所以引用不能改变指向。

6.3.4一个对象可有多个别名

前面的是多个对象一个别名,这里是一个对象多个别名。就好比一个人有多个外号一样。

int main()
{
	int a = 10;
	int& b = a;
	int& c = a;
	int& d = a;
	return 0;
}

在这里插入图片描述

6.4引用的如何使用

6.4.1引用做参数

实现两数交换,看以下代码:

void Swap(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}
int main()
{
	int a = 3;
	int b = 5;
	Swap(a, b);
	cout << a << endl << b << endl;
	return 0;
}

在这里插入图片描述
我们再来看以前写法,用指针:

void Swap(int* pa, int* pb)
{
	int tmp = *pa;
	*pa = *pb;
	*pb = tmp;
}
int main()
{
	int a = 4;
	int b = 7;
	Swap(&a, &b);
	cout << a << endl << b << endl;
}

在这里插入图片描述
通过比对,是不是很容易看出引用做参数更简洁,而且程序可读性更好。

6.4.2引用做返回值

第一种写法:引用返回值

int& Func(int a, int b)
{
	int c = a + b;
	return c;
}
int main()
{
	int ret = Func(1, 2);
	cout << ret << endl;
	return 0;
}

在这里插入图片描述
程序有以下错误:
在这里插入图片描述
分析:
该代码是两数之和返回c,返回的是c的别名。
在这里插入图片描述
我们再来看一段代码:

int& Func(int a, int b)
{
	int c = a + b;
	return c;
}
int main()
{
	int ret = Func(1, 2);
	cout << ret << endl;

	Func(3, 4);
	cout << ret << endl;

	return 0;
}

在这里插入图片描述
变量ret它接收的是返回值c的别名,c的别名是什么,这是编译器处理的,我们不需要管。但是我们知道,函数结束后,这个作用域就会被销毁。而这个c的别名返回给ret具体等于多少,取决于编译器是否清理完。如果清理完,就是随机值,否则就是a+b的大小,这是不确定的值,在VS下是还未清理完。再次调用这个函数,因为变量ret是有单独占一块空间的,它前面接收第一次调用的返回值就是3,而第二次调用函数完这个作用域销毁了,ret也没有去接收第二次调用的返回值,所以ret的内容不改变,还是3

第二种写法:接收的变量也是引用对象,ret前加&

int& Func(int a, int b)
{
	int c = a + b;
	return c;
}
int main()
{
	int& ret = Func(1, 2);
	cout << ret << endl;

	Func(3, 4);
	cout << ret << endl;

	return 0;
}

运行结果:
在这里插入图片描述
分析:
在这里插入图片描述
此时ret也是一个别名,是返回值的别名,与前面有所不同的是,ret不单独占内存空间,直接来说ret就是c的别名。前面提过,c的别名传过去是不确定的值,取决于编译器是否清理完。所以第一次调用在VS下是3,第二次调用在VS下是7

第三种写法:加static修饰
前面两种写法都是错误的,因为不同的编译器下实现的结果是不同的。所以这里得用static,可以使修饰的变量生命周期变长。

int& Func(int a, int b)
{
	static int c = a + b;
	return c;
}
int main()
{
	int& ret = Func(1, 2);
	cout << ret << endl;

	Func(3, 4);
	cout << ret << endl;

	return 0;
}

运行结果:
在这里插入图片描述
为什么两个都是3呢?因为static修饰的静态变量只能被初始化一次,也就是说函数第一次调用时被初始化为3(a+b=3),后面第二次调用时,它的初始值就是3了,如果再次static int c = a + b 相当于第二次初始化,这是不行的。

稍微调整下:

int& Func(int a, int b)
{
	static int c;
	c = a + b;
	return c;
}
int main()
{
	int& ret = Func(1, 2);
	cout << ret << endl;

	Func(3, 4);
	cout << ret << endl;

	return 0;
}

运行结果:
在这里插入图片描述
初始化后面为 c = a+ b就可以对C 进行修改

6.4.3常引用

const 是修饰常变量,引用的对象前面加const修饰或者引用前加const会怎样呢?

int main()
{
	const int a = 1;
	int& b = a;
	return 0;
}

在这里插入图片描述
const变量a,说明变量a变成了常变量,不能改变了。而b是a的别名,如果改变b就相当于把a改了,这就把权限放大了。

权限不可以放大,但可以缩小或者平移
权限缩小:

int main()
{
	int a = 1;
	const int& b = a;
	return 0;
}

注意:此时b就不能再改变了。

权限平移:两个都加上const

int main()
{
	const int a = 1;
	const int& b = a;
	return 0;
}

6.4.4常引用和类型转换

我们知道,引用必须两者是同类型的才行,如果不同编译器会报错

int main()
{
	int a = 3;
	double& b = a;
	return 0;
}

在这里插入图片描述
这个错误提示,发生类型转换时,引用的对象必须是常量。

增加const 就没问题了

	const double& b = a;

因为只要发生类型转换,都会产生一个临时变量,这个临时变量就是a要转换的结果,a本身是不变的,然后临时变量赋给b。为什么加const就可以了呢?因为产生的临时变量具有常属性,所以要加const。

6.5引用与指针

6.5.1引用和指针的异同

语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。
看以下汇编代码:
在这里插入图片描述

6.5.2不同点

  1. 引用是一个变量的别名,指针存储一个变量的地址
  2. 引用必须初始化,指针可以不用
  3. 引用不能改变指向的对象,指针可以
  4. 有多级指针,没有多级引用
  5. 没有NULL引用,有NULL指针
  6. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
  7. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  9. 引用使用起来比指针更安全

7.内联函数

7.1什么是内联函数

用关键字inline修饰的函数叫内联函数,编译时会在调用内联函数的地方展开,不用建立栈帧,提高了程序运行效率。

// inline 关键字
inline int Add(int a, int b)
{
	int c = a + b;
	return c;
}
int main()
{
	int ret = Add(3, 5);
	cout << ret << endl;
	return 0;
}

7.2为什么要有内联函数

普通函数会建立栈帧的开销,不用函数,改成宏虽然不用建立栈帧,但是宏的缺点也显而易见。

宏的缺点:
语法细节多,容易出错
不能调试
没有安全类型的检查

所以C++引入了内联这个概念,可提高程序的效率。

7.3内联函数的特性

7.3.1提高程序运行效率

如果编译器将函数当成内联函数来处理,在编译阶段,会用函数体替换函数调用。少了调用开销,提高效率;但缺点是可能会使目标文件变大。

7.3.2inline 只是一个建议

当我们使用inline时,只是给编译器发送这么一个请求而已,不同编译器的处理结果不同,也就是说是否会将函数转换成内联函数取决于编译器。一般建议,如果函数规模较小、不是递归且频繁调用可以使用inline修饰。
在这里插入图片描述

7.3.3内联不能声明和调用分离

分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

#include <iostream>
using namespace std;
inline int Add(int a, int b);
//
#include "List.h"
int Add(int a, int b)
{
	int c = a + b;
	return c;
}
/
int main()
{
	int ret = Add(3, 5);
	cout << ret << endl;
	return 0;
}

可看以下错误提示:
在这里插入图片描述

8.auto关键字

8.1介绍auto

简单来说,auto 就是拿来作类型推导,也可以叫做是类型的别名。

int a = 10;
auto b = a;

auto推导出来的类型是int

8.2为什么有auto

在编程时,我们返回一个函数的返回值,要知道它的返回类型。可是如果一个函数的返回类型很长,写起来很繁琐怎么办,所以用auto可以代替长的返回类型。

vector<string> v;
vector<string>::iterator it = v.begin();//写法1
auto it = v.begin();//写法2

8.3 auto的使用规则

1.auto与指针和引用结合起来使用
用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

    int x = 10;
    auto a = &x;
    auto* b = &x;
    auto& c = x;

2.在同一行定义多个变量
当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

auto a = 1, b = 2; 
auto c = 3, d = 4.0;  // 该行代码会编译失败,因为c和d的初始化表达式类型不同

注意:auto不能作为函数的参数;不能直接用来声明数组

9.指针空值nullptr(C++11)

在C语言中,我们学习过NULL是一个空指针。NULL实际是一个宏,可看以下代码:

#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

在编程时,如果没有给一个指针具体指向,那么就用NULL作为初始化的值。但是,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量,不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如:

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

程序本意是想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,因此与程序的初衷相悖。

在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0。

注意:

  1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
  2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
  3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。

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

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

相关文章

Linux:用户和权限

Linux&#xff1a;用户和权限 1. 认知root用户1.1 root用户&#xff08;超级管理员&#xff09;1.2 su和exit命令1.3 sudo命令1.3.1 为普通用户配置sudo认证 2. 用户、用户组管理2.1 用户组管理2.2 用户管理2.3 getent命令 3. 查看权限控制3.1 认知权限信息3.1.1 案例 4. 修改权…

react 实战- 玩转 react 全家桶(进阶)学习

一个命令是怎么运行起来的? Shell运行一个命令,实际上是运行一个脚本 环境变量 装了node以后,node的路径,就被注册到了环境变量里. 一个js的东西,可以注册? bin Webpack配置 构建 import A from A , const Arequire(A) 为什么可以这么写?为哈都行?本质上,是构建工…

人人自媒体的时候,Ai绘画还值得踏入吗?

前言 先说结论&#xff0c;如果你不打算涉足自媒体&#xff0c;平时也从不上网发什么内容去展示自己的话&#xff0c;其实AI绘画对你来说意义不大。但如果你对自媒体感兴趣&#xff0c;会涉及发作品&#xff0c;发内容&#xff0c;甚至去设计图片&#xff0c;那么AI绘画值得你…

【【萌新的SOC学习之自定义IP核的学习与设计】】

萌新的SOC学习之自定义IP核的学习与设计 本章为了更加深入的了解FPGA的自定义IP和IP封装测试等问题 参考了正点原子 第六讲自定义IP核呼吸灯实验 和 第十九章 IP封装与接口定义实验 为了更好的理解自定义IP核 我们先介绍一个带AXI主从接口的IP核 我们可以展开AXI从接口 下…

Vue--》简易资金管理系统后台项目实战(后端)

今天开始使用 node vue3 ts搭建一个简易资金管理系统的前后端分离项目&#xff0c;因为前后端分离所以会分两个专栏分别讲解前端与后端的实现&#xff0c;后端项目文章讲解可参考&#xff1a;前端链接&#xff0c;我会在前后端的两类专栏的最后一篇文章中会将项目代码开源到我…

转行做程序员,多晚都不晚

大家好啊&#xff0c;我是董董灿。 最近有不少小伙伴加我微信咨询一些问题&#xff0c;有同学想了解AI行业的现状&#xff0c;想着转行的&#xff0c;也有在校生想了解毕业后工作方向的&#xff0c;当然也有想学习编程知识的。 诚惶诚恐&#xff0c;没想到之前写的文章&#…

【CHI】CHI协议,transaction事务汇总

前言 CHI协议最难的是什么&#xff0c;就是那一堆各种各样的事务&#xff0c;你不知道什么场景应该使用什么合适的事务&#xff0c;收到X事务又该回复什么事务。相当于CHI给你制定了很多种&#xff08;尽可能覆盖完全&#xff09;场景及事务&#xff0c;你需要去了解&#xff0…

英语——分享篇——每日200词——2401-2600

2401——moisture——[mɔɪstʃə(r)]——n.潮气&#xff0c;湿气&#xff0c;水分——moisture——moist潮湿的(熟词)ur你的(编码your)e鹅(编码)——潮湿的地方你的鹅一身潮气——Moisture in the atmosphere condensed into dew during the night.——大气中的水分在夜间凝结…

二阶系统时域响应

二阶系统微分方程 二阶系统传递函数 二阶系统单位阶跃响应 过阻尼系统 临界阻尼系统 欠阻尼系统 无阻尼系统 二阶系统阶跃响应仿真 在Matlab中进行仿真&#xff0c;设置不同阻尼比2、1、0.5和0&#xff0c;可以得到结论&#xff1a; 阻尼比越小&#xff0c;系统响应速度越快&…

YOLOV8改进:RefConv(即插即用!重参数化重聚焦卷积替代常规卷积,无额外推理成本下涨点明显!)

1.该文章属于YOLOV5/YOLOV7/YOLOV8改进专栏,包含大量的改进方式,主要以2023年的最新文章和2022年的文章提出改进方式。 2.提供更加详细的改进方法,如将注意力机制添加到网络的不同位置,便于做实验,也可以当做论文的创新点。 3.涨点效果:RefConv,实现有效涨点! 论文地址…

【项目设计】网络对战五子棋(上)

想回家过年… 文章目录 一、项目前置知识1. websocketpp库1.1 http1.0/1.1和websocket协议1.2 websocketpp库接口的前置认识1.3 搭建一个http/websocket服务器 2. jsoncpp库3. mysqlclient库 二、 项目设计1. 项目模块划分2. 实用工具类模块2.1 日志宏封装2.2 mysql_util2.3 j…

蓝桥杯每日一题2023.10.21

后缀表达式 - 蓝桥云课 (lanqiao.cn) 题目描述 题目分析 30分解法&#xff1a;要求出最大的结果就需要加的数越大&#xff0c;减的数越小&#xff0c;以此为思路简单列举即可 #include<bits/stdc.h> using namespace std; typedef long long ll; const int N 2e5 10…

物联网知识复习

物联网的内涵和体系结构 物联网的基本内涵 物联网的基本内涵在于物联&#xff0c;物物相连或者物和人相连的互联网。 也就是说&#xff0c;它是要由物主动发起的&#xff0c;物物互联的互联网。 它的第一层意思是说物和物相连&#xff1b;第二层意思是说物和人相连。 物联网的…

Gradient conjugate priors and multi-layer neural networks

动机 先验参数 m , α , β , v m,\alpha,\beta,v m,α,β,v和随机变量 τ \tau τ KL散度的形式是&#xff1a; Dynamics of m , α , β , v m,\alpha,\beta,v m,α,β,v Dynamics of m , β , v m,\beta,v m,β,v for a fixed α \alpha α 绿色轨迹连接初始点和目标点…

【linux】Linux 查看内存使用情况的几种方法汇总

文章目录 GUI 查看命令获取命令 free命令 vmstat命令 top命令 htop Linux 查看内存使用情况的几种方法包括使用 free 命令、top 命令、htop 命令、vmstat 命令和/proc/meminfo 文件。这些方法可以帮助用户了解系统内存的使用情况&#xff0c;包括总内存、已用内存、空闲内存、缓…

MapperStruct实现类为空

​ 问题描述&#xff1a; MapperStruct生成的实现了为空 按照在MapperStruct官网Installation – MapStruct中的方法配置后&#xff0c;生成的实现了是空的&#xff0c;如下&#xff1a; Overridepublic DeployHistory toEntity(DeployHistoryDto arg0) {if ( arg0 null ) …

经典题型---旋转数组

经典题型—旋转数组 文章目录 经典题型---旋转数组一、题目二、代码实现 一、题目 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 示例 1: 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步…

EPLAN_010#STEP格式_箱柜模型的定义、拼柜

一、导入 首先创建一个宏项目——在布局空间中导航器新建一个布局空间 菜单栏——布局空间——导入(3D图形&#xff09;——导入下载下来的STEP 如果导入进来的箱柜是这种模样的&#xff0c;表示可以使用。如果左侧只显示一个逻辑组件&#xff0c;则无法使用。&#xff08;如果…

webgl计算包围盒大小

使用three.js&#xff1b; 代码&#xff1b; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>第一个three.js 示例</title><style>body {margin: 0;overflow: hidden;}</style><…

canvas绘制动态视频并且在视频上加上自定义logo

实现的效果&#xff1a;可以在画布上播放动态视频&#xff0c;并且加上自定义的图片logo放在视频的右下角 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthd…