深入理解C++指针

相信C++中指针是大家比较容易出错和难以理解的地方,本文尝试从内存地址的角度,作一些探讨。

首先让我们来看一段小代码:

1.        #include<iostream>

2.        using namespace std;

3.        void main()

4.        {

5.                 //字符指针指向一个字符串

6.                 cout << "字符指针测试部分:"<< endl;

7.                 char*p = "csdn";//定义一个字符型的指针,并用一个字符串初始化

8.                 cout << p << endl;//输出p所指向的字符串的值,C++中默认输出整个字符串

9.                 cout << (void*)p << endl;//输出指针p指向的地址

10.             cout << *p << endl;//输出p指向地址所存储的字符

11.             cout << (void*)&(*p) << endl;//输出该字符的地址

12.             cout << *(++p) << endl;//输出p指针指向的地址的下一个地址的存储的值

13.             cout << (void*)& *(++p) << endl;//输出该字符的地址

14.            

15.             cout << "字符数组测试部分:"<< endl;

16.             //字符数组存储一个字符串

17.             char s[] = "csdn";//定义一个字符型数组,并用一个字符串初始化

18.             cout << s << endl;//输出s所指向的字符串的值,C++中默认输出整个字符串

19.             cout << (void*)s << endl;//输出该字符数组的首地址

20.             cout << *s << endl;//输出s所代表地址的存储的值

21.             cout << &s << endl;//输出s所代表的地址

22.             cout << s[1] << endl;//输出s的下一个地址的存储值

23.             cout << (void*)&s[1] << endl;//输出该字符的地址

24.             }

运行结果:


结果分析:

从程序的分析结果来看,我们对字符串的存储可以用两种方式,当我们用指针存储时,p指针指向的是”csdn” 这个字符串的首地址,即是”c” 的地址,我们可以使用指针操作,对字符串中的字符进行访问并输出,当我们使用字符数组进行存储时,我们也可以看到数组名其实就标识这个字符串的首地址,我们同样也可以通过改变数组的下标来对字符串中的字符分别进行访问并输出。

那么,这两者的区别在哪呢?相信细心的亲已经发现,同样是对字符串中下一个字符进行访问,但使用指针存储时,地址偏移量是2个字节,当使用数组存储时,地址偏移是1个字节。而我们知道,字符类型的数据是占用一个字节的,那么问题出在哪呢?还有一个问题是,既然数组名是和一个地址绑定的,那么是否可以将数组名当作一个指针来进行处理呢?细心的亲可能发现,我在对下一个字符的遍历时,对数组和指针是不一样的,这又说明了什么呢?

我们先来看看数组名和指针的区别:

上代码!

用指针进行字符串的存储:

1.        #include<iostream>

2.        using namespace std;

3.        void main()

4.        {

5.                 char*p = "csdn";//定义一个字符型的指针,并用一个字符串初始化

6.                 cout << (void*)p << endl;//输出指针p指向的地址

7.                 cout << &p << endl;//输出存储指针变量p的地址空间

8.                 cout << *(++p) << endl;//输出p指针指向的地址的下一个地址的存储的值

9.                 cout << (void*)& *(++p) << endl;//输出该字符的地址

10.    }

运行结果:


结果分析:

指针p是一个变量,它有独立的存储空间,其存储空间存储的内容就是字符串的第一个字符的地址,我们可以通过指针的自增来完成字符串的遍历,我们也可以输出该指针变量的独立地址。

用字符数组进行存储:

1.        #include<iostream>

2.        using namespace std;

3.        void main()

4.        {

5.                 char s[] = "csdn";//定义一个字符型数组,并用一个字符串初始化

6.                 cout << (void*)s << endl;//输出指针p指向的地址

7.                 cout << &s << endl;//输出存储指针变量p的地址空间

8.                 cout << *(++s) << endl;//输出p指针指向的地址的下一个地址的存储的值

9.                 cout << (void*)& *(++s) << endl;//输出该字符的地址

10.    }

运行结果:

 

我们将代码修改一下:

#include<iostream>

1.          using namespace std;

2.          void main()

3.          {

4.                   char s[] = "csdn";//定义一个字符型数组,并用一个字符串初始化

5.                   cout << (void*)s << endl;//输出指针p指向的地址

6.                   cout << &s << endl;//输出存储指针变量p的地址空间

7.          }

运行结果:


通过以上分析我们知道,数组名a只是编译器在编译运行过程,给数组起始地址绑定的一个临时的标识符,它不是变量,也没有额外的存储空间,它无法进行自增操作,也没法输出额外的地址空间。

在此基础上,我们再来探讨一个更为深刻的问题,通过数组分配的地址空间和通过指针分配地址空间的赋值属性是一样的吗?在此基础上我们更深刻的认识,赋值操作的本质是什么?

保持优良传统,我们仍然通过代码来研究:

1.        #include<iostream>

2.        using namespace std;

3.        void main()

4.        {

5.                 char s[] = "csdn";//定义一个字符型数组,并用一个字符串初始化

6.                 *s = 'A';//修改字符串第一地址的字符值

7.                 cout << s << endl;//重新输出字符串

8.                 char*p = "csdn";//定义一个字符型的指针,并用一个字符串初始化

9.                 *p = 'A';// 修改字符串第一地址的字符值

10.             cout << p << endl; //重新输出字符串

11.    }

运行结果:


结果分析:

很明显,程序在执行到第9句时,发生错误,程序中断。这表明通过数组名绑定的地址的值是可以直接修改的,但通过指针指向的地址,是无法直接修改的,编译器会报出写入位置访问冲突。为什么会这样呢,我们不妨再看一段代码:

1.        #include<iostream>

2.        using namespace std;

3.        void main()

4.        {

5.                 char s[] = "csdn";//定义一个字符型数组,并用一个字符串初始化

6.                 cout << &s << endl;//输出s绑定的地址值

7.                 char*p = s;//定义一个字符型的指针,并把s绑定的值赋给p

8.                 cout << (void*)p << endl;//输出指针变量p存储的值

9.                 *p = 'A';//利用指针变量修改字符串的首字母

10.             cout << p << endl;//输出修改后的字符串

11.    }

运行结果:


神奇的事情发生了,这次我们竟然通过*p=’A’成功的修改了字符串的第一个字符,看来问题并不是出在指针上,而是出在地址空间本身上。我们知道计算机中的数据都是存储在地址空间上,就相当于一个个的地址单元,我们不妨把它看作图书馆用来存箱包的柜子。显然,我们在使用某个地址空间时,先得进行申请,不是随便一个柜子你都能用。

地址就相当于标识每个柜子的ID,当然有些时候为了方便,你可以在某个柜子上贴上“此柜有主”标签,那么这个柜子就归你所有了,你可以在其中放数据,也可以修改其中的内容,这个标签我们就可以把它看作是一个变量名。那么指针呢,指针是啥,指针可以看作是一个柜子管理员的登记表,这个登记表里面的记录就是一个个柜子的ID,你可以通过这个登记表去找到某个柜子。上面两个例子,在本质上说明一个问题,当使用数组申请一块空间时,这块空间相当于是可以被使用的,因为它是有变量的,已经分配到具体变量了(在字符数组中,其实是a[0],a[1],a[2],a[3],a[4])。但使用指针申请的一块数组,从本质上来说,它并没有分配给某个具体的变量,只是说在管理员的登记本上登记了这些箱子里面有哪些东西,但是这些箱子还是公家的,不能随便动。你可能会反问,它不是属于指针变量P吗?你仔细想想,这肯定是不对的,属于指针变量的地址空间,除了放一个个地址外,难道还可以放其他东西吗?显然这块区域不具有变量的特性,或者干脆把它看作一块常量区,一块由指针指向的常量区。


  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值