首页 > 编程语言 >C++开发基础——指针与引用

C++开发基础——指针与引用

时间:2024-03-24 23:30:32浏览次数:27  
标签:const 变量 int value 引用 指针 C++

一,关于指针

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

相关文章

  • C/C++练习
    1.数组升序#include<iostream>usingnamespacestd;//冒泡排序voidbubbleSort(int*arr,intlen){ for(inti=0;i<len-1;i++){ for(intj=0;j<len-1-i;j++){ if(arr[j]>arr[j+1]){ inttmp=arr[j]; arr[j]=arr[j+......
  • C++ 类的内存分配是怎么样的?
    dynamic_memory首先通过一段代码来引入动态内存分配的主题。一个名为StringBad的类以及一个功能更强大的String类。#include<iostream>#ifndefSTRNGBAD_H_#defineSTRNGBAD_H_classStringBad{private: char*str; intlen; staticintnum_strings;public: StringB......
  • C++ 设计模式
    C++设计模式工厂模式:我们需要方便的使用这些类,减少耦合度#include<iostream>#include<string>usingnamespacestd;classcar{public:car(stringname):name_(name){}virtualvoidshow();protected:stringname_;};classaodi:publiccar{public: aod......
  • LeetCode第390场周赛题解(c++)
    真的无语了,早上怎么都提交不了,显示未知错误。。。结果晚上就可以提交了。唉100245.每个字符最多出现两次的最长子字符串给你一个字符串 s ,请找出满足每个字符最多出现两次的最长子字符串,并返回该子字符串的 最大 长度。示例1:输入: s="bcbbbcba"输出: 4解释:以......
  • c++十大排序——快速排序
    1#include<iostream>usingnamespacestd;voidquickSort(intarr[],intbegin,intend){ if(begin>=end)return; intleft=begin; intright=end; inttemp=arr[left]; while(left<right){ //从后往前找比他小的放前面,从前往后找比它大的放后......
  • 【C++】每日一题 452 用最少数量的箭引爆气球
    有一些球形气球贴在一堵用XY平面表示的墙面上。墙面上的气球记录在整数数组points,其中points[i]=[xstart,xend]表示水平直径在xstart和xend之间的气球。你不知道气球的确切y坐标。一支弓箭可以沿着x轴从不同点完全垂直地射出。在坐标x处射出一支箭,若有......
  • 初识C++(二)引用,内联函数,auto
    目录1.引用的概念与用法:1.1引用特性:1.2使用场景    1.2.1做参数1.3传值、传引用效率比较1.4引用做返回值1.5引用和指针的对比2.内联函数3.auto关键字4.基于范围的for循环(C++11)5.指针空值nullptr(C++11)1.引用的概念与用法:    引用是一个重要的......
  • 指针的学习
    .指针:一个变量的地址指针变量:存放指针(变量地址)的变量& 取变量的地址单目运算符*取指针所指向变量的内容int*i_point,i; i=10; i_point=&i; printf("%x\n",&i);//变量对其取地址结果62fe14 printf("%x\n",&i_point);//对指针变量取地址,指针变量也需要空间存......
  • 代码解析 C++ 的语法难点
    C++是一种强大的编程语言,它提供了丰富的语法特性,但也带来了相应的语法难点。本文将深入解析C++中一些常见的语法难点,并提供清晰的解释和示例,帮助读者深入理解C++的语法。1.指针和引用指针和引用都是C++中处理内存地址的强大工具,但它们也容易混淆。指针指向内存地......
  • 第十二届蓝桥杯省赛C&C++ 研究生组
    十二届省赛题第十二届蓝桥杯省赛C&C++研究生组-卡片第十二届蓝桥杯省赛C&C++研究生组-直线第十二届蓝桥杯省赛C&C++研究生组-货物摆放第十二届蓝桥杯省赛C&C++研究生组-路径第十二届蓝桥杯省赛C&C++研究生组-时间显示第十二届蓝桥杯省赛C&C++研究生组-砝码称重......