C++入门学习五 指针和引用

第六章 指针和引用

这章介绍指针和引用,指针和引用是面向对象编程的基础,十分重要,能把这章彻底搞清楚,对C++的理解就会更加升深入。

什么是指针?

程序的每个变量和函数都位于内存的某个地方,指针是可储存地址的变量。

定义指针一定要初始化它,如果不能提供初始值,就初始化为nullptr。

类型名后面跟一个*号,表示定义一个该类型的指针,而不是变量。

#include <iostream>
using namespace std;

int main()
{
	int* p {nullptr}; // 定义一个int类型的空指针
} 

注意:指针的大小是固定的,取决于平台是32位还是64位。32位的指针大小是4,64位指针大小是8。


#include <iostream>
using namespace std;

int main()
{
	int* p{ nullptr }; // 定义一个int类型的空指针
	double* p1{ nullptr }; 
	cout << sizeof(p); // 8
	cout << sizeof(p1); // 8
} 

地址运算符&

它可以获取变量的地址

间接运算符*,也叫解引用运算符(解除指针的引用)

将间接运算符作用于指针,可以访问指针指向内存位置的数据。

#include <iostream>
using namespace std;

int main()
{
	int a = 1996;
	int* p = &a;
	cout << *p; // 1996
} 

指针的优点:

1、动态地为新变量分配内存空间

2、数组名指针表示法

3、访问函数外部大块数据

4、多态性的基础

char类型的指针

特性:可以用字符串字面量初始化,表示把字符串字面量的第一个字符的数据储存在指针中。注意要用const,以为字符串字面量是常量,不能修改。

输出一个指针指向的字符串,只需要输出该指针名。

#include <iostream>
using namespace std;

int main()
{
	const char* p{ "Hello" };
	cout << *p << endl; // H
	cout << p << endl; // Hello
} 

指针数组

#include <iostream>
using namespace std;

int main()
{
	const char* p[]{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
	cout << *p[0] << endl; // M
	cout << p[0] << endl; // Mon
} 

常量指针和指向常量的指针

指向常量的指针:例如const char* p {"Hello"};不可以修改"Hello"的值,p[0] = 'M'会报错,但是可以修改指针指向的内容p = "World"

常量指针:int a{10}; int* const p{&a}; 地址的内容可以修改 *p = 20,但是指向的地址不可修改,例如,int b {20}; p = &b;会报错

指向常量的常量指针:地址和值都不可以修改

#include <iostream>
using namespace std;

int main()
{
	// 指向常量的指针
	const char* p{"Hello"};
	p = "World"; // 不可以p[0] = 'M'

	// 常量指针
	int a{ 20 };
	int b{ 40 };
	int* const p1{ &a };
	*p1 = 21;
	cout << *p1; // 不可以p1 = &b;

	// 指向常量的常量指针
	const int c = 10;
	const int* const p2{ &c }; // 地址和值都不可以修改
} 

指针和数组

#include <iostream>
using namespace std;

int main()
{
	int a[10];
	int* p{ a }; // 数组名表示地址
} 

指向数组的指针加1,表示指向数组的下一个元素,表达式p+n,表示p中的地址加上n*sizeof(指针指向的类型)

#include <iostream>
using namespace std;

int main()
{
	int a[10]{10,20,30,40,50,60,70,80,90,100};
	int* p{ a }; // 数组名表示地址
	cout << *p << endl; // 10
	cout << *p + 1 << endl; // 11
	cout << *(p + 1) << endl; // 20
} 

指针之差

#include <iostream>
using namespace std;

int main()
{
	int a[10]{10,20,30,40,50,60,70,80,90,100};
	int* p1{ &a[0] };
	int* p2{ &a[2] };
	cout << p2 - p1 << endl; // 2
	cout << boolalpha << (p2 > p1) << endl; // true,说明p2索引更高
} 

数组名指针表示法

int a[5] {};

*(a),*(a+1),...表示数组中第一个,第二个,...元素

#include <iostream>
using namespace std;

int main()
{
	int a[5]{};
	for (int i = 0; i < 5; i++) {
		*(a + i) = i * i;
		cout << a[i] << endl;
	}
} 

动态内存分配

动态内存分配是在运行期间分配存储数据所需要的内存空间,而不是在编译时分配预定义的内存空间。

栈和自由存储区

自动变量(局部作用域变量)在执行其定义时创建。

栈:在内存区域给自动变量分配的空间。

在自动变量块尾,会释放栈上变量的所占据的内存。

自由存储区:操作系统和当前加载的其他程序未占用的内存

可以使用new把自由存储区的内存分配给一些变量,使用delete来释放

注意:delete释放内存后,没有改变指针,指针仍包含已分配的内存地址,但是该地址已经自由了,所以指针包含一个伪造的地址,称为悬挂指针,会造成严重的问题,所以总应该在释放指针后,重置该指针。

#include <iostream>
using namespace std;

int main()
{
	double* a{}; // 定义一个double类型的空指针
	a = new double{ 3.14 }; // 在自由存储区给一个double类型变量分配内存,并初始化为3.14
	cout << *a;
	delete a; // 释放
	a = nullptr; // 防止悬挂,重置该指针
} 

通过指针选择成员

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	auto* p{ new vector<int>{} };
	(*p).push_back(66); 
	p->push_back(77);// 更高级的写法,p->push_back(66),先解除对象的引用,再添加对象
} 

动态内存分配的危险

悬挂指针和多次释放:释放指针注意重置指针

分配与释放不匹配:注意每个new必须对应一个delete,每个new[]对应一个delete[]

内存泄漏:使用完new没有释放

原始指针和智能指针

之前介绍的指针都是原始指针

智能指针包含在头文件<memory>,3种智能指针

unique_ptr<T>:对象唯一储存了地址

shared_ptr<T>:可以多个shared_ptr<T>包含或共享同一地址,在任何给定时刻,运行时知道包含shared_ptr<T>的个数,叫引用计数。创建对象引用计数递增,释放或指向另一地址会递减,没有包含给定地址,引用计数为0

weak_ptr<T>:会链接到shared_ptr<T>,不会递增shared_ptr<T>的引用计数

#include <iostream>
#include <memory>
using namespace std;

int main()
{
	unique_ptr<int> p{ new int{123} };
	auto p1{ make_unique<int>(234) }; // 一般使用make_unique
	cout << p1.get() << endl; // 访问地址
	p1.reset(new int {456}); // 重置为456
	p1.reset(); // 重置为nullptr
	p1.release(); // 将智能指针转换为原始指针,一般不要这么写!!!
} 

引用&

相当于一个变量的别名

#include <iostream>
#include <memory>
using namespace std;

int main()
{
	int a{ 222 };
	int& b{a};
	b += 1;
	cout << a << endl; // 223
	cout << b << endl; // 223
} 

在基于范围的for循环中使用引用可以修改值,如果不想修改,可以用const引用 

#include <iostream>
using namespace std;

int main()
{
	// 不修改元素的值
	int a[5]{ 5,6,7,8,9 };
	int sum1{};
	for (int i : a) {
		i += 1;
		sum1 += i;
	}
	cout << sum1 << endl;
	for (int i = 0; i < 5; i++) {
		cout << a[i] << " ";
	}
	// 5,6,7,8,9
	cout << endl;

	// 使用引用修改了元素的值
	int b[5]{ 5,6,7,8,9 };
	int sum2{};
	int sum{};
	for (int& i : b) {
		i += 1;
		sum2 += i;
	}
	cout << sum2 << endl;
	for (int i = 0; i < 5; i++) {
		cout << b[i] << " ";
	}
	// 6,7,8,9,10
	cout << endl;

	// 使用const引用,不允许修改值
	int c[5]{ 5,6,7,8,9 };
	int sum3{};
	for (const int& i : b) {
		sum3 += i;
	}
	cout << sum3 << endl;
	for (int i = 0; i < 5; i++) {
		cout << c[i] << " ";
	}
	// 5,6,7,8,9
	cout << endl;
} 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值