用最简单的案例带你掌握C++中各种指针

1、前言

指针,作为C/C++中最神秘、功能最强大的语法,着实是难以理解 、难以掌握、难以运用。😥
但是,能灵活的使用指针,对利用C/C++开发程序将有很大的帮助,让我们一起来了解了解吧。

2、啥是指针?

指针就是一个变量,一个存放着某个内存地址的变量,此变量需要开发者手动释放,否则会造成内存泄漏。作为程序猿,内存泄漏应该不陌生吧。

在这里插入图片描述
从上图可以看出,指针的本质就是一个变量,存放着某个变量的内存地址,
定义一个指针:

int a = 10;
int* point = &a;
  1. 定义指针:类型 * 变量名 = & 变量名;
  2. &为取址符(引用),获取某个变量的地址;
  3. *为解引用符,用于解出指针指向的内存地址。
  4. 任何类型的指针在32位处理器上是4个字节,在64为处理器上为8个字节。

3、指针的基本操作

3.1、常量指针

常量指针本质就是指向某个常量的指针。
指针的指向可以改变,但是指针指向的值不能改变,因为值是一个常量

#include <iostream>
using namespace std;

const int sum = 10;
const int number = 20;

int main() {

    const int* p = &sum; // 定义一个指针指向一个常量
    cout << *p << endl;
    p = &number; // 改变指针的指向
    cout << *p << endl;
    // *p = 10; // 此处会报错,因为改变了常量的值。
    return 0;
}

3.2、指针常量

指针常量本质是指针类型的常量。
指针的指向不可以改变,但是指针指向的值可以修改。

#include <iostream>
using namespace std;

int main() {
    int a = 10, b = 20;
    int* const point = &a;
    cout << *point << endl; // 输出 10
    //point = &b; // 报错, 不能改变指针常量的指向
    *point = 30;
    cout << *point << endl; // 输出 30
    return 0;
}

在C/C++中有几个常见的指针常量:

  1. this指针,指向当前对象,通过它可以访问当前对象的所有成员。
  2. &变量(引用变量),可以改变变量的值,但是不能改变变量的指向。
  3. 数组名,数组名本质也是一个指针,指向内存的首地址,不能改变指向,但是可以改变每一个元素的值,

3.3、既是常量指针又是指针常量

既不能改变指针的指向,也不能改变指向指向的内存变量的值。

#include <iostream>
using namespace std;

int main() {
    const int a = 10;
    const int* const point = &a;
    cout << *point << endl;

    // 1、指向普通的变量
    int b = 20;
    point = &b; // 报错

    // 2、指向其他常量
    const int c = 30;
	point = &c;  // 报错

    //3、改变指针指向的内存中的值
    *point = 40; // 报错
    
    return 0;
}

3.4、指针作为函数的返回值

语法:返回值类型* 函数名称(参数类型 参数值)

#include <iostream>
using namespace std;

int* function(int a, int b){
    int* sum = new int;
    *sum = a + b;
    return sum;
}

int main() {
    int* result = function(10, 20);
    
    cout << *result << endl; // 输出 30
    
    return 0;
}

当函数返回的类型是指针类型的时候,返回值就必须是在堆上开辟的内存空间,绝对不能返回函数中的局部变量,如果返回了局部变量就会报异常:

address of local variable ‘a’ returned [-Wreturn-local-addr]

那么为什么返回局部的指针变量不会报错?
不是不会报错,而是需要分情况而定,如果局部指针指向的是一个局部的普通变量,那么也同样会报错,因为指针的数据还是在栈上开辟,而栈上的数据在函数 执行完毕后就会被释放。

int* function(int a, int b){
    int c = a + b;
    int* sum = &c;
    return sum; // 报错
}

而如果指针指向的数据是由new关键字开辟在堆上的数据,就不会报错。

int* function(int a, int b){
    int* sum = new int;
    *sum = a + b;
    return sum;
}

3.5、指针做函数的形参

语法:返回值类型 方法名称(指针类型* 变量名称)

#include <iostream>
using namespace std;

int function(int* a, int* b){
   int sum = (*a + *b);
   return sum;
}

int main() {
    int a = 10, b = 20;
    int result = function(&a, &b);
    cout << result << endl; // 输出 30
    return 0;
}

只要是指针,然后符合语法规范,便可以变成常量指针、指针常量等。
而在发开中,我们也推荐使用指针作为函数的形参:
在C/C++中,我们可以使用sizeof关键字看到某个对象所占用的内存空间,当对象的属性非常多或者属性所占的内存比较多的时候,函数的形参也会复制实参的所有属性在内存中开辟相应的内存空间,那么此时就会十分的消耗内存。
上面我们了解到:任何类型的指针变量就只占四个或者八个字节,所以我们可以使用指针作为函数的形参,以此来减少内存的开销。

#include <iostream>
#include "headers/Person.hpp"
#include "string"
using namespace std;

int main() {
    cout << sizeof(int *) << endl; // 输出 8
    cout << sizeof(Person<string,string> *) << endl; // 输出 8

    return 0;
}

因为我的电脑是64位的笔记本,所以任何指针类型的变量都只占8个字节。

4、引用类型(&引用指针)

引用指针本质就是一个指针常量,在有的书上也称为别名,是一个变量的副本,
引用的特点:

  1. 引用变量就是某个变量或者对象的别名
  2. 引用变量不占用内存
  3. 引用变量必须在声明时完成赋值,完成变量与引用的绑定
#include <iostream>
#include "headers/Person.hpp"
#include "string"
using namespace std;

int main() {

    Person<string,string> person = Person<string ,string>("10001","abc"),
            &point = person;
    cout << sizeof(point) << endl; // 输出 64
    cout << sizeof(person) << endl; // 输出 64

    return 0;
}

不是说引用不占用内存空间吗?为什么引用和变量所占用的内存空间是一样的咧?
因为在表达式中,使用引用实际上就像使用变量本身一样,所以直接用sizeof是得不到引用本身的大小的。
但是我们可以查看地址,看引用的地址是否和变量的地址是同一个。

int main() {
    Person<string,string> person = Person<string ,string>("10001","abc"),
            &point = person;
    cout << &person << "---->" << &point << endl;
    return 0;
}

输出:

0xb00a9ff830---->0xb00a9ff830
可以看出是同一个地址。

4.1、引用类型作为函数的返回值

语法:返回值类型& 函数名(形参类型 形参名称)
好处:最大的好处是在内存中不产生返回值的副本

#include <iostream>
using namespace std;

int& function(int a, int b){
    int sum = a + b;
    return sum;
}

int main() {
    cout << function(10, 20) << endl;
    return 0;
}

此段程序报错,这里和用指针做函数的返回值是一样的错误,因为引用的本质还是指针,所以不能返回在栈上开辟的内存。
如果把function中的sum这个局部变量变为全局变量,那么这段程序就是正确的,或者使用new 在堆上开辟内存。

#include <iostream>
using namespace std;

int& function(int a, int b){
    int* sum = new int;
    *sum = (a + b);
    return *sum;
}

int main() {
    cout << function(10, 20) << endl; // 输出 30
    return 0;
}

4.2、引用类型作为函数的形参

语法: 返回值类型 方法名(类型& 变量名)

#include <iostream>
using namespace std;

int function(int& a, int& b){
   return a + b;
}

int main() {
    int a = 10, b = 20;
    cout << function(a, b) << endl;  // 输出 30
    return 0;
}

当我们使用引用类型作为函数的形参的时候,需要注意一个点:当我们修改了引用的值的时候,其实本质变量的值也会发生改变,因为引用只是一个别名,是会修改本质变量的值的。

#include <iostream>
#include "string"
using namespace std;

int function(int& a, int& b){
    int sum = a + b; 
    a = 20;
    b = 10;
   return sum;
}

int main() {
    int a = 10, b = 20;
    cout << function(a, b) << endl;  // 输出 30
    cout << "a=" << a << ", b=" << b;  
    //输出a=20, b=10   此时a 和 b的值发生了交换。
    //所以当别名修改了值之后,本质变量的值也会发生修改。
    return 0;
}

5、总结

  1. 指针作为C/C++中的重点知识,还是要弄明白的好,不然大佬写的代码实在难以看懂。😰😨
  2. 指针的本质上也就是对内存的应用,但不是开辟内存,开辟内存还得使用calloc等函数,这两大点也就是这门语言令人着迷的地方,通过源码可以看到C/C++中大量的使用了指针,熟悉好这些内容这门语言也算是入了门了。
  3. 指针和引用本身都是一样的,只不过是引用相当于对指针进行了一层封装,知识点也差不多,只是多多少少存在一些坑,慢慢理解就行了。
  4. 以上是本人的个人观点,有不足之处还望指出🙂🙂🙂
    在这里插入图片描述
  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
智能指针C++的一种特殊类型的指针,它能够自动管理内存的释放,避免了手动释放内存的繁琐和容易出错的问题。C++11引入了几种智能指针,包括引用计数的智能指针和不引用计数的智能指针,如auto_ptr, scoped_ptr, unique_ptr, shared_ptr, weak_ptr等。\[1\] 引用计数的智能指针(shared_ptr)使用引用计数来跟踪指针的引用次数,当引用计数为0时,自动释放内存。这种智能指针适用于多个指针共享同一块内存的情况,可以避免内存泄漏和悬挂指针的问题。 不引用计数的智能指针(unique_ptr)则采用了独占所有权的方式,确保每个指针只能指向一个对象。当指针超出作用域或被显式释放时,内存会被自动释放。这种智能指针适用于需要独占资源的情况,如动态分配的内存、文件句柄等。 自己实现智能指针可以通过重载指针操作符和使用引用计数等技术来实现。但需要注意的是,自己实现智能指针需要考虑线程安全性和异常安全性等问题,确保指针的正确释放和资源的正确管理。 然而,需要注意的是,智能指针并不能解决所有的问题。例如,当存在循环引用时,引用计数的智能指针(shared_ptr)可能导致内存泄漏。此外,使用智能指针时也需要注意避免裸指针和智能指针混用的情况,以免出现悬挂指针或重复释放内存的问题。\[2\]\[3\] 总之,深入掌握C++智能指针需要了解各种智能指针的原理、用法和适用场景,并注意使用智能指针的注意事项,以确保代码的正确性和安全性。 #### 引用[.reference_title] - *1* *2* [【C++】深入掌握智能指针](https://blog.csdn.net/weixin_45853856/article/details/121184992)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [722-深入掌握C++智能指针](https://blog.csdn.net/LINZEYU666/article/details/120982178)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值