【C++】C++入门基础

C++(C plus plus) 是一种计算机高级程序设计语言,既可以进行 C语言过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行以继承和多态为特点的面向对象的程序设计。


文章目录

  • 前言
  • 一、C++ 的第一个程序
  • 二、命名空间(namespace)
    • 2.0 C语言的不足
    • 2.1 namespace 的作用
    • 2.2 namespace 的定义
    • 2.3 命名空间的使用
  • 三、C++ 输入输出
  • 四、缺省参数
  • 五、函数重载
  • 六、引用(&)
    • 6.1 引用的概念和定义
    • 6.2 引用的特性
    • 6.3 引用的使用
    • 6.4 const 引用
      • 6.4.1 const 引用的引入
      • 6.4.1 const 引用的介绍
      • 6.2.2 const 引用的价值
    • 6.5 指针和引用的关系
  • 七、内联函数(inline)
  • 八、空指针(nullptr)
  • 总结


前言

关于 C++ 的发展历史

在这里插入图片描述

C++ 从 C++98 开始,总共发布了三个大版本:C++98、C++11、C++20。
从 C++98 开始,引入了 STL(标准模板库),使 C++ 比 C语言 更加方便好用,C++ 就从 C   w i t h   c l a s s e s C\ with\ classes C with classes 逐渐变成一门全新的语言。
C++11 又进行了一次革命性的更新,更新了许多 C++ 全新的特性,使 C++ 更加像一门全新的语言,因此我们主要学习C++11的内容。

在这里插入图片描述
从 C++11 之后,基本上是每三年更新一个版本,使 C++ 不断引入新的特性。

总结一下,现代 C++ 语言可以看作是三部分组成的:

  1. 低级语言:大部分继承自C语言。
  2. 现代高级语言特性:允许我们定义自己的类型以及组织大规模程序和系统。
  3. 标准库:它利用高级特性来提供有用的数据结构和算法。

一、C++ 的第一个程序

学习一门语言之前,肯定都会先去学习如何输出 h e l l o   w o r l d hello\ world hello world,基本上和我们学英语先学打招呼 s e e   h e l l o see\ hello see hello
是一个道理。

C++ 的 h e l l o   w o r l d hello\ world hello world 程序如下:

#include<iostream> //包含头文件 输入输出流

using namespace std;//全部展开 命名空间 标准库std 

int main()
{
	cout << "hello world!" << endl;	//控制台console 换行 end line
	
	return 0;
}

可见和C语言有很大不同,总共以下几个方面:

  1. 头文件 :#include<iostream>
  2. 命名空间:using namespace std;
  3. 输入输出:cout << "hello world" << endl;

接下来将依次介绍C++的一些新语法。


二、命名空间(namespace)

2.0 C语言的不足

在C语言中,如果函数名和变量名相等,会之间编译报错:

#include<stdio.h>
#include<stdlib.h>	//头文件里面包含rand()函数

int rand = 10;	

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

C语言库 s t d l i b stdlib stdlib 中有对 r a n d ( ) rand() rand() 函数的定义,因此我们在定义 r a n d rand rand 变量时会和 r a n d ( ) rand() rand() 函数命名冲突,直接报错了:

在这里插入图片描述

这个问题会使我们在命名的时候很不方便,因此C++采用了命名空间来解决这个问题:

2.1 namespace 的作用

在 C/C++ 中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于
局作用域
中,可能会导致很多冲突。都将名字放置在全局命名空间中将引发命名空间污染

传统上,程序员会将全局实体名字起的很长来避免命名冲突问题,这显然不太理想:对于程序员来说,书写和阅读这么长的名字费时费力且过于繁琐。

命名空间( n a m e s p a c e namespace namespace防止名字冲突提供了更加可控的机制:

  1. 命名空间分割了全局命名空间,其中每个命名空间是一个作用域
  2. 通过在某个命名空间中定义变量、函数和类的名字,访问某个值时只需要访问所对应的命名空间即可。

2.2 namespace 的定义

一个命名空间的定义包含两部分:首先是关键字 n a m e s p a c e namespace namespace,随后是命名空间的名字

  1. 命名空间只能在全局定义。

比如定义一个加法命名空间,里面定义的全都是和加法有关的变量和函数:

namespace add
{
	//变量
	int x = 10, y = 20;

	//函数
	int Add(int x, int y)
	{
		return x + y;
	}
	
	//结构体
	struct Node
	{
		int val;
		struct Node* next;
	}
}
  1. 命名空间可以嵌套定义

比如让加法命名空间里再定义一个减法运算:

namespace add
{
	//变量
	int x = 10, y = 20;

	//函数
	int Add(int x, int y)
	{
		return x + y;
	}
	
	//结构体
	struct Node
	{
		int val = 0;
		struct Node* next = nullptr;
	};

	//命名空间
	namespace sub
	{
		int x = 30, y = 20;

		int Sub(int x, int y)
		{
			return x - y;
		}
	}
}

这样就可以在不同的命名空间定义名字相同的变量

  1. 只要能出现在全局作用域中的声明就能置于命名空间内,主要包括:
    – (1) 类
    – (2) 变量(及其初始化操作)
    – (3) 函数(及其定义)
    – (4) 模版
    – (5) 其他命名空间

  2. 每个命名空间都是一个作用域
    在 C++ 中,域包括:函数局部域全局域命名空间域类域
    域影响的是编译时语法查找⼀个变量/函数/类型出处(声明或定义)的逻辑,所以有了域隔离,名字冲突就解决了。
    局部域和全局域除了会影响编译查找逻辑,还会影响变量的生命周期,命名空间域和类域不影响变量生命周期。

  3. 命名空间可以是不连续的
    项目工程中多文件中定义的同名 n a m e s p a c e namespace namespace 会认为是一个 n a m e s p a c e namespace namespace,不会冲突。

p s ps ps:C++ 标准库都放在⼀个叫 s t d ( s t a n d a r d ) std(standard) std(standard) 的命名空间中。

2.3 命名空间的使用

编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。

因此,要使用命名空间中定义的变量/函数,有三种方式:

  1. 指定命名空间访问。(项目推荐)

域操作限定符( : : :: :::通过 “ : : :: :: ” 来访问命名空间里的成员。

//访问add里的x
cout << add::x << endl;
//访问sub里的x
cout << add::sub::x << endl;

//访问add里的Add()函数
cout << add::Add(1,2) << endl;
//访问sub里的Sub()函数
cout << add::sub::Sub(2,1) << endl;

//访问add里的结构体Node
struct add::Node node;
cout << node.val << endl;
  1. 使用 u s i n g using using 将命名空间中某个成员展开。(项⽬中经常访问的不存在冲突的成员推荐)
//访问add里的x
using add::x;
cout << x << endl;
//访问sub里的x
using add::sub::x;
cout << x << endl;

//访问add里的Add()函数
using add::Add;
cout << Add(1,2) << endl;
//访问sub里的Sub()函数
using add::sub::Sub
cout << Sub(2,1) << endl;

//访问add里的结构体Node
using add::Node;
struct Node node;
cout << node.val << endl;
  1. 使用 u s i n g using using 将命名空间中全部成员展开。(项目不推荐)
using namespace add;
//访问add里的x
cout << x << endl;
//访问add里的Add()函数
cout << Add(1,2) << endl;
//访问add里的结构体Node
struct Node node;
cout << node.val << endl;

using namespace add::sub;
//访问sub里的x
cout << x << endl;
//访问sub里的Sub()函数
cout << Sub(2,1) << endl;

三、C++ 输入输出

C++ 语言不直接处理输入输出,而是通过一组定义在标准库中的类型来处理 I O IO IO

C++ 对于输入输出的标准库 < i o s t r e a m > <iostream> <iostream> ,全称为 I n p u t   O u t p u t   S t r e a m Input\ Output\ Stream Input Output Stream输入输出流),定义了标准的输入、输出对象。
ps:<iostream> 也间接包含了 <stdio.h>,因此也可以直接使用 printf 和 scanf。

  1. s t d : : c i n std::cin std::cin i s t r e a m istream istream 类的对象,主要面向窄字符的标准输入流。
  2. s t d : : c o u t std::cout std::cout o s t r e a m ostream ostream 类的对象,主要面向窄字符的标准输出流。
  3. s t d : : e n d l std::endl std::endl 是一个函数,流插入输出时,相当于换行 + + +刷新缓冲区
  4. > > >> >> 是流提取运算符,用于输入数据: c i n > > cin >> cin>>
  5. < < << << 是流插入运算符,用于输出数据: c o u t < < cout << cout<<

c i n cin cin c o u t cout cout p r i n t f printf printf s c a n f scanf scanf 相比,好处是写法简单,而且会自动识别类型,非常便捷。

注意:不管是 c o u t cout cout 还是 p r i n t f printf printf 都会先转换为字符串,然后输出。

在像算法竞赛这种需要大量输入输出数据的场景中,往往 c i n cin cin c o u t cout cout 的效率远不及 p r i n t f printf printf s c a n f scanf scanf,不过我们还可以通过下列三行代码来关闭同步流,来加速 c i n cin cin c o u t cout cout

ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);

四、缺省参数

缺省参数声明或定义一个函数的时候给形参赋值,在调用函数的时候如果实参个数比形参少,则会自动使用形参开始时赋的值(也称默认值)。

缺省参数根据形参赋值的个数分为:

  1. 全缺省:全部形参给缺省值。
void Func(int a = 1, int b = 2, int c = 3)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
	cout << endl;
}
int main()
{
	//全缺省
	Func();
	Func(4);
	Func(4,5);
	Func(4,5,6);
	return 0;
}

打印结果为:

在这里插入图片描述

由此可见,全缺省就是全部形参都给赋值,这样不论实参的个数是多少,都能采用默认值。
同时,给实参的顺序是从左到右的。

  1. 半缺省(部分缺省):部分形参给缺省值。
void Func(int a, int b = 2, int c = 3)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
	cout << endl;
}
int main()
{
	//半缺省(此时最少给一个参数)
	Func(4);
	Func(4,5);
	Func(4,5,6);
	return 0;
}

运行结果为:

在这里插入图片描述

由此可见,半缺省就是部分形参赋值,部分形参没赋值,并且只能是从左到右连续没赋值(不能跳跃)!
而且,有几个形参没赋值,实参就最少给几个参数。(保证每个参数都有值)
同理,给实参的顺序也是从左到右的。

总结 缺省参数有以下几个性质:

  1. C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。
  2. 带缺省参数的函数调用,C++规定必须从左到右依次给实参,不能跳跃给实参。
  3. 函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现

有一个很形象的记忆点,叫:做人不要做缺省参数(bushi),要做实参。缺省参数相当于备胎,舔狗,当没有实参的时候,我们才会采用缺省参数。


五、函数重载

C++⽀持在同⼀作用域中出现同名函数(不同作用域用命名空间),但是要保证其形参不同,可以是参数个数不同或者类型不同。(表现出了多态的行为,更加灵活)

int Add(int a, char b)
{
	b -= '0';
	return a + b;
}

//1.参数个数不同
int Add(int a, int b, int c)
{
	return a + b + c;
}

//2.参数类型不同
double Add(double a, double b)
{
	return a + b;
}

//3.参数类型顺序不同
int Add(char a, int b)
{
	a -= '0';
	return a + b;
}

int main()
{
	cout << Add(1, '2') << endl;

	cout << Add(1, 2, 3) << endl;

	cout << Add(1.1, 2.2) << endl;
	
	cout << Add('1', 2) << endl;

	return 0;
}

输出结果为:

在这里插入图片描述

由此可见,函数重载只和参数有关,和返回值无关。
参数的个数类型顺序决定函数的种类,同名不同种函数,即为函数重载。


六、引用(&)

引用的出现可以说很大一部分取代了指针的作用 —— 我们函数传参的时候不用传地址了,直接传引用即可。

6.1 引用的概念和定义

引用不是新定义⼀个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间

类型& 引用别名 = 引用对象;

int a = 10;	//定义a变量

int& b = a;	//取a的引用b
int& c = a;	//取a的引用c
int& d = a;	//取a的引用d

d++; //改变引用实际上改变的是a

cout << a << " " << b << " " << c << " " << d << " " << endl;

输出结果如下:

在这里插入图片描述

可以看出所有引用都指向同一份空间 a a a,改变了引用即改变被引用变量的值。

当然也可以复合引用,即实现了多级指针的功能:

int a = 10;	//定义a变量

int& b = a;	//取a的引用b
int& c = b;	//取b的引用c
int& d = c;	//取c的引用d

d++;

cout << a << " " << b << " " << c << " " << d << " " << endl;

输出结果当然不变,都都相当于间接指向同一个空间 a a a

在这里插入图片描述

6.2 引用的特性

  1. 引用在定义时必须初始化
int a = 10;

// 编译报错:“ra”: 必须初始化引⽤
//int& ra;
  1. 一个变量可以有多个引用

可以参考 6.1 ,一个变量取多个别名,多个引用指向同一块空间。

  1. 引用一旦引用一个实体,再不能引用其他实体
int a = 10;

int c = 20;

int& b = a;	//取a的引用b

b = c;	//这里不是让b引用c,而是一个赋值

cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
cout << a << endl;
cout << b << endl;
cout << c << endl;

运行结果如下:

在这里插入图片描述

可见,得出以下信息:

  1. a a a b b b 指向同一块空间,而 c c c 自己单独一块空间:引用过的变量不能改变引用
  2. 最终是将 c c c 的值赋给了 b b b。(当然也相当于赋给了 a a a):引用过的变量只能赋值。

6.3 引用的使用

引用最大的用途就是函数的传参和返回值

引用传参跟指针传参功能是类似的,引用传参相对更方便⼀些:

  1. C语言中只能用指针传参:
void Swap(int* x, int* y)	//用指针变量来接收地址
{
	int tmp = *x;	//改变指针变量指向的值的时候还需要解引用
	*x = *y;
	*y = tmp;
}

int main()
{
	int x = 10, y = 20;

	Swap(&x, &y);	//需要传地址

	cout << x << " " << y << endl;

	return 0;
}

运行结果如下:

在这里插入图片描述

  1. C++中可以引用传参:
void Swap(int& x, int& y)	//直接取实参的引用作为形参,这样形参的改变会直接作用于实参
{
	int tmp = x;	//因为共用同一空间,所以直接改变引用即改变原变量
	x = y;
	y = tmp;
}

int main()
{
	int x = 10, y = 20;

	Swap(x, y);	//直接传实参即可

	cout << x << " " << y << endl;

	return 0;
}

运行结果如下:

在这里插入图片描述

可以看出,不管是指针还是引用,都很好的起到了形参的改变能直接作用于实参的作用。
但是显而易见的是,指针变量修改时不仅要解引用,函数调用的使用还需要取地址,非常繁琐。
反而引用调用只需要将形参改为引用类型,这样就简洁明了的起到了直接改变形参的作用。

6.4 const 引用

c o n s t const const 引用可以引用 const 对象,但是必须用 c o n s t const const 引用。
c o n s t const const 引用也可以引用普通对象,因为对象的访问权限在引用过程中可以缩小,但是不能放大。
c o n s t const const 引用还可以引用临时对象,因为临时对象具有常性。

6.4.1 const 引用的引入

我们都知道,定义一个常量(不可修改的值)可以用 c o n s t const const

const int a = 10;	//定义一个值为10的常量a

还知道,如果要修改一个值,目前主要有两种方法:

  1. 直接修改
int a = 10;

a = 20;
  1. 引用修改(这里省略指针修改,因为本质是一样的 —— 都是利用访问空间修改)
int a = 10;

int& b = a;
b = 20;

c o n s t const const 定义的值是不能被直接修改的。

6.4.1 const 引用的介绍

那么就有一个问题了,既然引用可以改变变量的值,那么常量可以有引用吗?

显然是不行的,既然是常量,那么其值是不希望被修改的,因此直接取常量会报错,因为权限不够:

const int a = 10;

// 编译报错:error C2440: “初始化”: ⽆法从“const int”转换为“int &”
int& b = a;

这里的引用是对 a a a 访问权限的放大。(权限不可以放大)

此时,可以用 c o n s t const const 引用来放大引用的权限。

const int a = 10;

const int& b = a;	//这样就可以了

// 编译报错:error C3892: “a”: 不能给常量赋值
a++;	//a不能被修改

// 编译报错:error C3892: “b”: 不能给常量赋值
b++;	//b也不能被修改

这里引用的权限是匹配的。

但如果引用是对 a a a 访问权限的缩小是可以的。(权限只能大变小,不能小变大)

int a = 10;

const int& b = a;	//对b访问权限的缩小

a++;	//a可以被修改

// 编译报错:error C3892: “b”: 不能给常量赋值
b++;	//b不能被修改

6.2.2 const 引用的价值

  1. 引用常量
// 编译报错:error C2440: “初始化”: ⽆法从“int”转换为“int &”
int& b = 30;	//普通引用没有权限给常量区别名

const int& b = 30;	//const int& 可以给常量取别名
  1. 引用表达式(引用临时对象)

表达式的值会先保存到临时对象里,再将临时对象里的值拷贝返回。但是临时对象具有常性。(相当于常量了)

const int a = 2;
int c = 1;

const int& b = a + c;	//将3赋给b
const int& d = a * 3;	//将6赋给b
  1. 隐式类型转换(引用临时对象)
double d = 3.14;

int a = d;	//会直接将d隐式类型转换成int型(向下取整)
const int a = d; //同理,向下取整

// 编译报错:“初始化”: ⽆法从“double”转换为“int &”
int& a = d;	

const int& a = d;	//会将d看作表达式,产生临时变量,进行隐式类型转换
  1. 作函数参数

在函数传参的时候我们可以直接传参:

void func(int val)	//如果不希望修改val可以写成const int val
{
	cout << val << endl;
}

int main()
{
	int a = 10;
	const int b = a * 3;
	const int& c = a;
	double d = 3.14;

	func(a);	//10
	func(b);	//30
	func(40);	//40
	func(a + b);//40
	func(c);	//10
	func(d);	//3

	return 0;
}

但是函数传参的时候,如果采用直接传参,那么会将实参拷贝给实参。
此时,如果拷贝变量很大(结构体或者),那么会很耗用内存。

因此,引用传参可以直接将实参传给函数,不用拷贝,大大节约内存。

但与之而来的一个问题:

如果直接传引用,有很多值都无法传参,例如常量、表达式、隐式类型转换,直接传参会产生编译错误:

void func(int& val)	//只能云序
{
	cout << val << endl;
}

int main()
{
	int a = 10;
	const int b = a * 3;
	const int& c = a;
	double d = 3.14;

	func(a);	//10
	func(b);	//error C2664: “void func(int &)”: 无法将参数 1 从“const int”转换为“int &”
	func(40);	//error C2664: “void func(int &)”: 无法将参数 1 从“int”转换为“int &”
	func(a + b);//error C2664: “void func(int &)”: 无法将参数 1 从“int”转换为“int &”
	func(c);	//error C2664: “void func(int &)”: 无法将参数 1 从“const int”转换为“int &”
	func(d);	//error C2664: “void func(int &)”: 无法将参数 1 从“double”转换为“int &”

	return 0;
}

这是我们就要采用 c o n s t const const 引用,很好的解决了上面的问题:

void func(const int& val)
{
	cout << val << endl;
}

int main()
{
	int a = 10;
	const int b = a * 3;
	const int& c = a;
	double d = 3.14;

	func(a);	//10
	func(b);	//30
	func(40);	//40
	func(a + b);//40
	func(c);	//10
	func(d);	//3

	return 0;
}

这样不仅解决了拷贝变量的消耗问题,也解决了很多值都无法传参的问题,两全其美。

6.5 指针和引用的关系

C++中指针和引用在实践中相辅相成功能有重叠性,但是各有自己的特点,互相不可替代

首先,指针和引用在底层(汇编层面)实际上是一样的,只是抽象出来让我们理解起来不一样。

但是在今后找工作的面试中,面试官可能会问:C++中指针和引用的区别是什么?
这里主要总结了六大区别:

  1. 语法概念:

引用是⼀个变量的取别名,不开空间;指针是存储⼀个变量地址,要开空间

  1. 初始化:

引用定义时必须初始化;指针建议初始化,但是语法上不是必须的。

  1. 作用对象个数:

引用在初始化时引用⼀个对象后,就不能再引用其他对象;而指针可以在不断地改变指向对象

  1. 访问对象:

引用可以直接访问指向对象;指针需要解引用才能访问指向对象。

  1. 所占字节大小:

引用所占字节大小为引用类型的大小;但指针始终是地址空间所占字节个数。( 32 32 32 位平台下占 4   b y t e 4\ byte 4 byte 64 64 64 位下占 8   b y t e 8\ byte 8 byte

  1. 安全性

指针很容易出现空指针和野指针的问题;引用很少出现,引用使用起来相对更安全⼀些。(函数返回引用类型的时候可能会返回已经销毁的临时变量的引用)


七、内联函数(inline)

i n l i n e inline inline 修饰的函数叫做内联函数 i n l i n e inline inline 适用于频繁调用的短小函数

  1. 内联函数的作用:

内联函数在编译时,C++编译器会在其调用的地方展开,这样调用内联函数就不需要建立栈帧了,就可以提高效率

注意 i n l i n e inline inline 对于编译器而言只是一个建议,也就是说,你加了 i n l i n e inline inline 编译器也可以选择在调用的地方不展开,不同编译器关于 i n l i n e inline inline 什么情况展开各不相同,因为 C++ 标准没有规定。(例如:递归函数代码相对多一些的函数,加上 i n l i n e inline inline 也会被编译器忽略)

  1. 内联函数的意义:

C语言实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂很容易出错的,且不方便调试。

C++设计了 i n l i n e inline inline 目的就是替代C的宏函数。

C语言宏函数替换

// 实现⼀个ADD宏函数的常见问题
//#define ADD(int a, int b) return a + b;
//#define ADD(a, b) a + b;
//#define ADD(a, b) (a + b)

// 正确的宏实现(要考虑展开后和运算符优先级的问题)
#define ADD(a, b) ((a) + (b))
// 为什么不能加分号?
// 为什么要加外面的括号?
// 为什么要加里面的括号?

int main()
{
	cout << ADD(1, 2) << endl;	//cout << ((1)+(2)) << endl;
	
	return 0;
}

C++内联函数替换

inline int add(int a, int b)
{
	return a + b;
}

int main()
{
	cout << add(1, 2) << endl;	//cout << 1 + 2 << endl;
	
	return 0;
}

肉眼可见的方便多了。

  1. 注意事项:

i n l i n e inline inline 不建议声明和定义分离到两个文件,分离会导致链接错误。因为 i n l i n e inline inline 被展开,就没有函数地址,链接时会出现报错。

//Add.h

#pragma once

#include<iostream>

using namespace std;

inline void Add(int a, int b);
//Add.cpp

#include"Add.h"

inline void Add(int a, int b)
{
	cout << a + b << endl;
}
//main.cpp

#include"Add.h"

int main()
{
	Add(1, 2);	//(链接错误)error LNK2019: 无法解析的外部符号 "void __cdecl Add(int,int)" (?Add@@YAXHH@Z),函数 main 中引用了该符号
	
	return 0;
}

如果需要分离,内联函数应直接放到 . h .h .h 文件中定义


八、空指针(nullptr)

n u l l p t r nullptr nullptrC++11 引入的特殊的关键字,用来替换 C语言 N U L L NULL NULL 来表示空指针

C C C 语言中的 N U L L NULL NULL 实际是⼀个宏,在传统的 C C C 头文件( s t d d e f . h stddef.h stddef.h)中,可以看到如下代码:

#ifndef NULL
	#ifdef __cplusplus
		#define NULL 0	//字面常量0 
	#else
		#define NULL ((void *)0)	//⽆类型指针(void*)的常量0
	#endif
#endif

在C++中 N U L L NULL NULL 被替换为 0 0 0
在C语言中 N U L L NULL NULL 被替换为 ( v o i d   ∗ )   0 (void\ *)\ 0 (void ) 0

不论采取何种定义,在使用空值的指针时,都不可避免的会遇到⼀些麻烦:

本想通过 f ( N U L L ) f(NULL) f(NULL) 调用指针版本的 f ( i n t ∗ ) f(int*) f(int) 函数,但是由于 N U L L NULL NULL 被定义成 0 0 0,调用了 f ( i n t   x ) f(int\ x) f(int x),因此与程序的初衷相悖。 f ( ( v o i d   ∗ ) N U L L ) ; f((void\ *)NULL); f((void )NULL); 调用会报错。

于是,C++11引入了 n u l l p t r nullptr nullptr 来定义空指针

  1. n u l l p t r nullptr nullptr 是⼀个特殊的关键字, n u l l p t r nullptr nullptr 是一种特殊类型的字面量,它可以转换成任意其他类型指针类型
  2. 使用 n u l l p t r nullptr nullptr 定义空指针可以避免类型转换的问题,因为 n u l l p t r nullptr nullptr 只能被隐式地转换为指针类型,⽽不能被转换为整数类型。

总结

以上就是 C++ 的发展历史,以及基于 C++11 相对于 C语言 在基础语法上来说主要有了哪些不同,主要是为后面学习类和对象的内容做铺垫。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

探索AI对冲基金:开源自动化交易系统的革新之路

在量化交易领域,人工智能技术的应用正悄然改变传统对冲基金的运作模式。GitHub上的开源项目ai-hedge-fund为开发者和金融从业者提供了一个独特的实践平台。该项目通过多智能体系统架构,整合市场数据分析、量化策略生成、风险管理和投资组合优化等核心功能,实现了从数据采集到…

C语言每日一练——day_3(快速上手C语言)

引言 针对初学者&#xff0c;每日练习几个题&#xff0c;快速上手C语言。第三天。&#xff08;会连续更新&#xff09; 采用在线OJ的形式 什么是在线OJ&#xff1f; 在线判题系统&#xff08;英语&#xff1a;Online Judge&#xff0c;缩写OJ&#xff09;是一种在编程竞赛中用…

SpringCloud系列教程(十三):Sentinel流量控制

SpringCloud中的注册、发现、网关、服务调用都已经完成了&#xff0c;现在就剩下最后一部分&#xff0c;就是关于网络控制。SpringCloud Alibaba这一套中间件做的非常好&#xff0c;把平时常用的功能都集成进来了&#xff0c;而且非常简单高效。我们下一步就完成最后一块拼图Se…

VMware安装欧拉操作系统(openEuler)第二节

摘要&#xff1a; 本篇文章接上篇《VMware安装欧拉操作系统&#xff08;openEuler&#xff09;第一节》&#xff0c;上一篇写到vmware workstation 17中创建openEuler虚拟机&#xff0c;本篇将详细介绍openEuler操作系统初始化以及相关配置的详细内容。 VMware安装欧拉操作系统…

[数据结构]并查集--C++版本的实现代码

目录 并查集的基本框架 查找一个元素在哪一个集合 判断两个元素是否在同一个集合 将两个集合进行合并 查询有多少组 测试 大学班级的同学会来自于五湖四海&#xff0c;每个人的家乡可能都不相同&#xff0c;那么如何将相同省份的同学连接到一块&#xff0c;也就是按省份进…

基于SpringBoot+Vue的瑜伽课体验课预约系统【附源码】

基于SpringBootVue的瑜伽课体验课预约系统 一、系统技术说明二、运行说明三、系统的演示四、系统的核心代码演示 一、系统技术说明 框架&#xff1a;SpringbootVue 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软…

【编译器】VSCODE烧录ESP32-C3——xiaozhi智能聊天机器人固件

【编译器】VSCODE烧录ESP32-C3——xiaozhi智能聊天机器人固件 文章目录 [TOC](文章目录) 前言一、方法一&#xff1a;使用固件烧录工具1. 安装CH340驱动2. 打开FLASH_DOWNLOAD文件3. 选择芯片类型和烧录方式4. 选择烧录文件5. 参数配置 二、方法二&#xff1a;VSCODE导入工程1.…

【C++】 —— 笔试刷题day_1

为了锻炼自己写代码的思路&#xff0c;开始每日刷题&#xff0c;加油&#xff01;&#xff01;&#xff01; 第一题 数字统计 题目要求&#xff1a; ​ 给定一个范围 [L , R] 求出数字L在该区间内出现的次数。&#xff08;其中1<L<R<10000&#xff09; 算法思路&…

R语言和RStudio安装

整体还是比较简单的&#xff0c;主要是记录个流程。 官方镜像站列表R语言官网 1 安装R&#xff08;2025/3/6&#xff09; R语言官网&#xff1a;The R Project for Statistical Computing 打开之后就Hello world一下吧 配置环境变量 2 安装RStudio 下载地址&#xff1a;htt…

计算机视觉应用|自动驾驶的感知革命:多传感器融合架构的技术演进与落地实践

一、引言 自动驾驶的终极目标是实现比人类驾驶更安全、更高效的交通系统。其核心挑战在于如何让机器像人类一样感知和理解复杂环境。然而&#xff0c;人类驾驶员依赖视觉、听觉和触觉的多模态信息&#xff0c;而自动驾驶系统则需要通过传感器和算法模拟这一过程。当前&#xf…

高效自动化测试:打造Python+Requests+Pytest+Allure+YAML的接口测试框架

一、背景 在快节奏的开发周期中&#xff0c;如何确保接口质量&#xff1f;自动化测试是关键。通过构建标准化、可复用的测试框架&#xff0c;能显著提升测试效率与准确性&#xff0c;为项目质量保驾护航[1][7]。 二、目标 ✅ 核心目标&#xff1a; ● 实现快速、高效的接口测试…

速算迷你世界脚本UI

--[[ --数学速算主界面 local UI"6996144362677448610" local v"6996144362677448610_" --自定义玩家数据界面 --显示界面分类 -- --称号积分幼儿园0学前班50小学生200初中生500高中生1000大学生2000研究生5000博士生10000教授50000 local A {["主屏幕…

『PostgreSQL』 Ubuntu 系统下PG15的安装与 PGVector 配置指南

&#x1f4e3;读完这篇文章里你能收获到 &#x1f4e6; 学会如何在 Ubuntu 上安装最新的 PostgreSQL 15 数据库。&#x1f511; 掌握修改 PostgreSQL 管理员密码和配置远程连接的方法。&#x1f310; 了解如何启用 PGVector 插件&#xff0c;实现向量存储和多种距离计算。&…

关于在electron(Nodejs)中使用 Napi 的简单记录

当我们使用electron想要集成一个C SDK实现很底层的算法逻辑就有可能与C SDK进行数据通信。 Napi 应该是比较好的选择&#xff0c;因为C本身的运行速度很快&#xff0c;使用Napi也能很大程度上保证软件的兼容性、又不会阻塞C线程、还可以很简单的与C 实现数据传递。 开始使用 安…

vscode(cursor)配置python环境,含远程调试

一、本地配置 1.1 安装python插件 1.2 配置python环境 在右下角就可以切换python环境&#xff0c;太简单了&#xff01; 1.3 Debug说明 打断点直接开启&#xff01; 在debug的过程中&#xff0c;还可以输入打印中间变量或者做一些测试 二、远程连接 2.1 下载远程工具 2.2 连…

S19文件格式详解:汽车ECU软件升级中的核心镜像格式

文章目录 引言一、S19文件格式的起源与概述二、S19文件的核心结构三、S19在汽车ECU升级中的应用场景四、S19与其他格式的对比五、S19文件实例解析六、工具链支持与安全考量七、未来趋势与挑战结语引言 在汽车电子控制单元(ECU)的软件升级过程中,S19文件(也称为Motorola S-…

怎么使用数据集微调大模型LLM

怎么使用数据集微调大模型LLM 目录 怎么使用数据集微调大模型LLM项目运行后目录结构1. 导入必要的库2. 准备训练数据3. 加载模型与分词器4. 数据预处理5. 配置训练参数(CPU 专用)6. 训练与保存完整可运行代码,调试了2天,保证可用项目运行后目录结构 1. 导入必要的库 from …

深度评测DeepSeek、ChatGPT O1和谷歌Gemini AI应用开发场景 - DeepSeek性能完胜!

下面我会展示我为期一周的实验结果&#xff0c;创作不宜&#xff0c;希望大家关注我&#xff0c;以后多多互3&#xff01;前一阵我在互联网上看到很多关于DeepSeek R1的讨论&#xff0c;这个开源模型据说可以媲美&#xff0c;甚至优于像OpenAI o1这样的付费模型。 由于我在日常…

ubuntu局域网部署stable-diffusion-webui记录

需要局域网访问&#xff0c;如下设置&#xff1a; 过程记录查看源码&#xff1a; 查看源码&#xff0c;原来修改参数&#xff1a;--server-name 故启动&#xff1a; ./webui.sh --server-name0.0.0.0 安装下载记录&#xff1a; 快速下载可设置&#xff1a; export HF_ENDPOI…

数智读书笔记系列015 探索思维黑箱:《心智社会:从细胞到人工智能,人类思维的优雅解读》读书笔记

引言 《The Society of Mind》&#xff08;《心智社会》&#xff09;的作者马文・明斯基&#xff08;Marvin Minsky&#xff09;&#xff0c;是人工智能领域的先驱和奠基者之一 &#xff0c;1969 年获得图灵奖&#xff0c;被广泛认为是对人工智能领域影响最大的科学家之一。他…