对C++指针的理解

指针

指针(Pointer)是一个变量,存放内存地址的变量。
变量名、函数名、字符串名、数组名等等只是地址的助记符而已,在编译之后都会被替换成相应的地址,指针名也会被替换成地址,该地址存放的数据也是地址。

指针的值(即地址)应属下列4种状态之一:
1.指向一个对象。
2.指向紧邻对象所占空间的下一个位置。
3.空指针,意味着指针没有指向任何对象。
4.无效指针,也就是上述情况之外的其他值。
试图拷贝或以其他方式访问无效指针的值都将引发错误。编译器并不负责检查此类错误,这一点和试图使用未经初始化的变量是一样的。访问无效指针的后果无法预计,因此程序员必须清楚任意给定的指针是否有效。
尽管第2种和第3种形式的指针是有效的,但其使用同样受到限制。显然这些指针没有指向任何具体对象,所以试图访问此类指针(假定的)对象的行为不被允许。如果这样做了,后果也无法预计。

指针的特点

  1. 指针变量可以进行加减运算,例如p++、p+i、p-=i。指针变量的加减运算并不是简单的加上或减去一个整数,而是跟指针指向的数据类型有关。

  2. 给指针变量赋值时,要将一份数据的地址赋给它,不能直接赋给一个整数,例如int *p = 1000;是没有意义的,使用过程中一般会导致程序崩溃。

  3. 使用指针变量之前一定要初始化,否则就不能确定指针指向哪里,如果它指向的内存没有使用权限,程序就崩溃了。对于暂时没有指向的指针,建议赋值nullptr。

  4. 两个指针变量可以相减。如果两个指针变量指向同一个数组中的某个元素,那么相减的结果就是两个指针之间相差的元素个数。

  5. 数组也是有类型的,数组名的本意是表示一组类型相同的数据。在定义数组时,或者和 sizeof、& 运算符一起使用时数组名才表示整个数组,表达式中的数组名会被转换为一个指向数组的指针。

  6. 请使用智能指针!!!

指针和引用

1、本质区别:
指针本质还是变量,是存放内存地址的变量,在逻辑上是独立的
它可以被改变,包括两种改变,
1、指针指向的改变
2、指向地址存放的数据的改变。

引用是对象的一个别名,不是对象,没有地址,它在逻辑上不是独立的,它是依附于变量而存在的,所以其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量)

2、重要区别:
引用定义时必须同时被初始化,指针虽然不强制初始化但是强烈建议初始化,实在不清楚指针指向何处也应该初始化为nullptr或0
引用必须初始化的原因:
一般在初始化变量时,初始值会被拷贝到新建的对象中。然而定义引用时,程序把引用和它的初始值绑定(bind)在一起,而不是将初始值拷贝给引用。一旦初始化完成,引用将和它的初始值对象一直绑定在一起。因为无法令引用重新绑定到另外一个对象,因此引用必须初始化。

引用和指针的其他重点:
1、只能指向严格匹配的对象
除了1、基类引用|指针指向派生类对象,2、const int&|const int*指向int 这两种情况,其他所有引用的类型都要和与之绑定的对象严格匹配。

而且,引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起
在这里插入图片描述
2、指针是对象,引用不是对象
引用不是对象,所以不存在指向引用的指针、不存在对引用的引用
但是指针是对象,所以存在指向指针的指针,而且还存在指向指针的引用

引用的实现原理

int a=1
int &b=a

在语言层面上b就是变量a的别名,对a和b的操作实际效果相同。

实现上b本质是一个指针,b中存放的是a的地址,并且是一个const指针(因为引用不能改变)

为什么有了指针还要设计引用?

因为指针很危险,使用不当很容易造成内存泄露,多级指针很容易把人搞糊涂了,引用相对而言就简单的多,安全的多。

指针函数 vs 函数指针

指针函数:一个返回指针的函数,其本质是函数
int *p(): 返回值类型为 int *
函数指针:指向函数的指针。本质是指针
int (*p)(): 指向原型为 int func() 的函数

指针数组 vs 数组指针

指针数组:本质是数组,数组里每个元素都是指针
定义:int *a[10];

数组指针:本质是指针,指向数组
定义:int (*a)[10];

特殊指针

有三种比较特殊的指针

空指针nullptr

空指针(null pointer)不指向任何对象,在试图使用一个指针之前代码可以首先检查它是否为空。以下列出几个生成空指针的方法:
在这里插入图片描述
当不知道指针初始化时指向哪里,就将其指向nullptr

注意:因为内存编号0 ~255为系统占用内存,不允许用户访问,所以空指针指向的内存是不可以访问的

	int * p = NULL;//指针变量p指向内存地址编号为0的空间
	cout << *p << endl;	//访问空指针报错 

void指针

void *表示一个有效指针,它确实指向实实在在的数据,只是数据的类型尚未确定,表示指针指向的数据的类型是未知的,在后续使用过程中一般要进行强制类型转换

C语言动态内存分配函数 malloc() 的返回值就是void *类型,在使用时要进行强制类型转换,如下:

 //分配可以保存30个字符的内存,并把返回的指针转换为 char *
char *str = (char *)malloc(sizeof(char) * 30);

野指针

两种情况下是野指针:

  • 指针在定义时未初始化,其值是随机的
  • 指针指向的内存空间被释放了

如何规避野指针呢?养成下面的良好的编程习惯:

  1. 指针变量如果暂时不需要赋值,一定要初始化为NULL
    因为任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的。

  2. 当指针指向的内存被释放掉时,要将指针的值设置为 NULL
    因为 free() 只是释放掉了内存,并为改变指针的值。

NULL和nullptr

NULL到底是什么?
NULL 并不是 C++ 的关键字,它是 C++ 为我们事先定义好的一个,并且它的值往往就是字面量 0(#define NULL ((void *)0))。

C++ 中将 NULL 定义为字面常量 0,虽然能满足大部分场景的需要,但可能存在二义性问题。例如:

#include <iostream>
using namespace std;
 
void func(int x) {
    cout<<"void func(int x)"<<endl;
}
 
void func(char *y) {
    cout<<"void func(int *y)"<<endl;
}
 
int main()
{
    func(NULL);
    return 0;
}

编译结果:

linuxy@linuxy:~/dirNULL$ g++ main.cpp -o main
main.cpp: In function ‘int main():
main.cpp:14:14: error: call of overloaded ‘func(NULL)’ is ambiguous
   14 |     func(NULL);
      |              ^
main.cpp:4:6: note: candidate:void func(int)4 | void func(int x) {
      |      ^~~~
main.cpp:8:6: note: candidate:void func(char*)8 | void func(char *y) {
      |      ^~~~
linuxy@linuxy:~/dirNULL$

从编译结果来看,显示程序有二义性,程序提示 func(NULL) 有两个可选项。

由于 C++ 98 标准使用期间,NULL 已经得到了广泛的应用,出于兼容性的考虑,C++11 标准并没有对 NULL 的宏定义做任何修改。

为了修正 C++ 存在的这一 BUG,C++ 标准委员会最终决定另其炉灶,在 C++11 标准中引入一个新关键字,即 nullptr
nullptr 是 nullptr_t 类型的右值常量,专用于初始化空类型指针
在 C++11 标准下,相比 NULL 和 0,使用 nullptr 初始化空指针可以令我们编写的程序更加健壮。

nullptr_t 是 C++11 新增加的数据类型,可称为“指针空值类型”。也就是说,nullpter 是nullptr_t 类型的一个实例对象(已经定义好,可以直接使用)

值得一提的是,nullptr 可以被隐式转换成任意的指针类型。举个例子:

int * a1 = nullptr;
char * a2 = nullptr;
double * a3 = nullptr;

显然,不同类型的指针变量都可以使用 nullptr 来初始化,编译器分别将 nullptr 隐式转换成 int*、char* 以及 double* 指针类型。

另外,通过将指针初始化为 nullptr,可以很好地解决 NULL 遗留的问题,比如:

#include <iostream>
using namespace std;
void isnull(void *c){
    cout << "void*c" << endl;
}
void isnull(int n){
    cout << "int n" << endl;
}
int main() {
    isnull(NULL); //int n
    isnull(nullptr); //void*c
    return 0;
}

可以看出,由于 nullptr 无法隐式转换为整形,但是可以隐式匹配指针类型

智能指针

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值