一,关于指针
1.指针的基础概念
指针是可存储地址的变量,存储在指针中的地址可以是变量或者其他数据的地址。
指针不仅仅是指向某地址,指针还关注指向该地址的数据类型。
例如:long* num_ptr {};
这里的num_ptr指针今后只能存储long类型的变量地址,尝试用它存储非long类型的变量地址将会产生编译报错。
注意,无论指针变量指向什么类型或者大小的数据,指针变量本身的大小是相同的,指针变量的大小仅仅取决于目标平台的可寻址内存的大小。例如,对于64位的计算机架构,指针变量的大小一般是8个字节。
2.指针的定义和使用
a.指针的初始化
未初始化的指针使用起来风险很大,因此,定义指针的同时必须初始化,如果不知道指定什么样的初始值,可以先初始化为nullptr。
因此,下面这段代码:
long* num_ptr {};
应该改为:
long* num_ptr {nullptr};
也可以使用自定义类型来初始化指针:
char16_t* char_ptr {nullptr};
b.指针的具体使用
(1)指针赋值
对指针变量使用操作符"="会改变指针的指向,所以,对指针采取赋值操作可以理解为指针方向的重定向。
例如,如果p1和p2是两个指针变量,"p2=p1"操作会让p2去指向p1当前正在指向的内存地址。
(2)指针的算术运算
算术运算的本质是让指针沿着一定的方向去移动指定大小的单位。
拿指针的加法运算举例,整数会先和指针所指向的类型大小(单位是字节)相乘,得到偏移量,然后指针的初始地址按照这个偏移量往前移动一定的单位。
3.地址运算符&
地址运算符"&"可以获取变量的内存地址,常用于指针变量的初始化。
&可以应用在任何类型的变量上面,然后用变量对应的指针类型去存储变量的地址。
例如要获得int类型变量的地址,则存储地址的指针必须也是int类型。
int num=10;
int* p_num=#
auto p_num=# //也可以让编译器判断类型
auto* p_num=# //auto*,让代码更清晰
4.解引用运算符*
间接运算符"*"可以获取内存地址所存储的对应的变量值。
因为"*"是通过内存地址来间接获取变量的值,所以称为间接运算符,通常也称它为解引用运算符。
符号"*"有时候是用来声明指针的,有时候是用来解引用的。
当它和类型放在一起,例如"int*",便是声明指针的;
当它和变量放在一起(前面没有加类型或者auto),例如"*p_value",便是解引用的。
下面这段处理逻辑相当于:"data_2 = data_1"。
ptr = &data_1;
data_2 = *ptr;
5.char类型与指针
代码样例:
char* char_ptr {"Hello"};
初始化char*类型指针的示意图
在C语言的写法中,char数组不能被改变,因此在C++的初始化代码中,需要在char*前面加const修饰符,避免编译报错。
const char* char_ptr {"Hello"};
注意,指向数值类型的指针必须解引用,才能拿到指针所指向的元素值。但是指向char类型的指针,可以不经过解引用,直接利用指针名获得元素的值。有时候,为了让代码更清晰,也会对char类型的指针做解引用操作。
完整C++代码实现:
#include <iostream>
using namespace std;
int main()
{
const char* char_ptr{ "Hello" };
std::cout << char_ptr[0] << std::endl;
std::cout << *char_ptr << std::endl;
std::cout << char_ptr[1] << std::endl;
std::cout << *(char_ptr + 1) << std::endl;
return 0;
}
运行结果:
H
H
e
e
6.数组与指针
a.指向数组的指针
该指针指向数组中的第一个元素。
当程序使用new分配一段内存块时,应使用delete来释放。但是当使用new创建数组时,应该使用"delete []"来释放数组。
int* p_array = new int [10];
delete [] p_array;
此时,访问该数组也很简单,可以把指针变量名当作数组名来使用。
完整C++代码实现:
#include <random>
#include <iostream>
#include <memory>
#include <functional>
int main()
{
int* p_array = new int[10];
p_array[0] = 11;
p_array[1] = 22;
std::cout << &p_array << std::endl;
std::cout << p_array << std::endl;
std::cout << p_array+1 << std::endl;
//指针变量当数组来用
std::cout << p_array[0] << std::endl;
std::cout << p_array[1] << std::endl;
//对数组指针解引用
std::cout << *p_array << std::endl;
std::cout << *(p_array+1) << std::endl;
delete [] p_array;
}
运行结果:
006CFCC4
00BEC170
00BEC174
11
22
11
22
b.指针数组
指针与数组还可以形成另一种结构,被称为指针数组,数组的元素都是指针类型。指针数组的使用可以节省操作时间,如果要交换数组中的元素,只需要交换彼此的指针就可以实现,避免了很多复制操作。
如图,基于指针数组实现的二维数组:
完整C++代码实现:
#include <iostream>
using namespace std;
int main()
{
int N = 3;
int** p = new int* [N];
int x = 1;
for (int i = 0; i < N; i++) {
p[i] = new int[N];
for (int j = 0; j < N; j++, x++) {
p[i][j] = 10 * x;
}
}
cout << *p << endl;
cout << **p << endl;
cout << *p + 1 << endl;
cout << **p + 1 << endl;
cout << *(*(p + 1) + 0) << endl;
cout << p[2][2] << endl;
return 0;
}
运行结果:
015420A8
10
015420AC
11
40
90
7.const与指针
const与指针结合使用,分下面三种情况:
(1)指向常量的指针——存储的值为常量,指针为变量。
指针所指向的常量值不可以被修改,但是指针可以被修改为指向其他常量的地址。
此时的指针常用来指向const类型的常量。
const int value {20};
const int* p_value=&value; //const类型的常量value
此时value是一个常量,它的大小不能被修改,但是可以修改p_value指向的地址,例如:
const int value_2 {30};
p_value=&value_2;
如果要禁止修改p_value指向的地址,可以在p_value前面再加一个const修饰符。
const int* const p_value=&value;
(2)常量指针——存储的值为变量,指针为常量。
常量指针只能指向初始化时指定的固定地址,此时虽然指针指向的地址不可以被修改,但是地址存放的变量值可以被修改。
此时的指针常用来指向非const类型的变量。
int data {20}; //此时不能用const
int* const p_data=&data;
*p_data = 30; //data的值被修改为30
(3)指向常量的常量指针——存储的值为常量,指针为常量
此时,指针被声明为常量,且指向的是不可被修改的常量。指针指向的地址,地址存放的值,都不可以被修改。
const float value {3.1415f};
const float* const p_value=&value;
完整C++代码实现:
Demo1:
#include <iostream>
int main()
{
int x{ 5 };
int y{ 6 };
int* const ptr{ &x };
ptr = &y;
return 0;
}
运行结果:
编译报错:
“ptr”: 不能给常量赋值
Demo2:
#include <iostream>
int main()
{
int x{ 5 };
int* const ptr{ &x };
std::cout << x << std::endl;
std::cout << &x << std::endl;
*ptr = 6; //x的地址不变,值被改为6
std::cout << x << std::endl;
std::cout << &x << std::endl;
return 0;
}
运行结果:
5
004FFA60
6
004FFA60
二,关于引用
1.引用的基础概念
引用的用法和指针类似,引用可以看作是某变量的别名,变量的引用在用法上和变量完全对等。
2.引用和指针的不同点
1.引用被声明时必须被初始化。 一般用另外一个变量来初始化引用,使引用成为该变量的别名。
2.引用不能在中途被修改为指向别的变量,一旦引用被初始化为某个变量的别名,那么在这个引用的生命周期内,将一直引用该变量。
引用和指针的使用,在很多场景下减少了拷贝的操作次数,增加了代码的运行效率。
3.引用的初始化
引用的定义方式有些类似于指针,指针在定义的时候,是类型名+"*",引用在定义的时候,是类型名+"&"。
关于"&"的位置,指针初始化时,"&"放在赋值语句右边;引用初始化时,"&"放在赋值语句左边。
引用可以和原始变量保持一样的使用方式,不需要解引用。
int value_1 {20};
int value_2 {20};
int* p_value=&value_1; //指针的初始化
int& r_value=value_2; //引用的初始化
*p_value += 10; //解引用
r_value += 10; //没有解引用
注意,引用中的"&"也被当作是地址运算符,"r_value"是变量value_2的别名,"&r_value"是指向变量value_2的地址。
完整C++代码实现:
#include <random>
#include <iostream>
#include <memory>
#include <functional>
int main()
{
int value_1{ 20 };
int value_2{ 20 };
int* p_value = &value_1; //指针的定义
int& r_value = value_2; //引用的定义
*p_value += 10; //解引用
r_value += 10; //没有解引用
std::cout <<"value_1: " << value_1 << std::endl;
std::cout <<"value_2: " << value_2 << std::endl;
std::cout <<"data of value_1: " << *p_value << std::endl;
std::cout <<"address of value_1: " << p_value << std::endl;
std::cout <<"data of value_2: " << r_value << std::endl;
std::cout <<"address of value_2: " << &r_value << std::endl;
}
运行结果:
value_1: 30
value_2: 30
data of value_1: 30
address of value_1: 0048FAE8
data of value_2: 30
address of value_2: 0048FADC
4.函数的引用传参
函数传参有两种方式:
1,按值传递:
传参样例:funtion_name(int param1)
传递的是值,实际上传入的是原始变量的一个副本,因此不会修改原始变量的值。
2,按引用传递:
传参样例:funtion_name(int& param2)
传递的是引用,实际上传入的是指向原始变量的一个指针,因此会修改原始变量的值。
因此,引用传参的主要目的有:
为了在调用函数的时候,顺带修改原始变量的值。
为了在调用函数的时候,减少变量副本的生成。
5.函数的const引用传参
很多开发场景经常这样使用,函数在按引用传递参数的同时,加入了const修饰符。
传参样例:funtion_name(const int& param3)
const引用传参的特点:
1.向函数传入的是指向原始变量的一个指针,避免了原始变量的副本生成。
2.利用const修饰符,不允许修改原始变量。
const引用传参的主要目的是为了提升代码效率,因为它既不会像按值传递那样,会拷贝一个副本出来,也不会像按引用传递那样,原始变量值会在函数调用期间被任意修改。
完整C++代码实现:
#include <iostream>
using namespace std;
void swap_1(int& x, int& y);
void swap_2(const int& x, const int& y);
int main() {
int a = 100;
int b = 200;
cout << "Before swap_1, value of a :" << a << endl;
cout << "Before swap_1, value of b :" << b << endl;
swap_1(a, b);
cout << "After swap_1, value of a :" << a << endl;
cout << "After swap_1, value of b :" << b << endl;
swap_2(a, b);
cout << "After swap_2, value of a :" << a << endl;
cout << "After swap_2, value of b :" << b << endl;
return 0;
}
void swap_1(int& x, int& y) {
int temp;
temp = x;
x = y;
y = temp;
return;
}
void swap_2(const int& x, const int& y) {
int temp;
temp = x;
/*
这样操作会产生编译报错
x = y;
y = temp;
*/
return;
}
运行结果:
Before swap_1, value of a :100
Before swap_1, value of b :200
After swap_1, value of a :200
After swap_1, value of b :100
After swap_2, value of a :200
After swap_2, value of b :100
三,参考阅读
《Beginning C++17, 5th Edition》
《C++ Primer Plus》
《C++高级编程》
https://en.cppreference.com/w/cpp/language/pointer
https://cplusplus.com/doc/tutorial/pointers/
https://www.programiz.com/cpp-programming/pointers-arrays
https://www.geeksforgeeks.org/creating-array-of-pointers-in-cpp/
标签:const,变量,int,value,引用,指针,C++ From: https://blog.csdn.net/CoderZZ_2024/article/details/136994670