指针
知识点
指针的概念
指针保存了一个内存地址,对指针的操作就是对地址的操作。
可以将内存理解为一个“大数组”,指针相当于存储了一个数组下标,它指向下标对应位置的变量。
指针的声明
用以下代码声明指针。其中,&运算符被称为取地址运算符,它返回变量在内存中的地址。
void指针可以指向任意类型的值。
nt a; int *ptrToA = &a; double b; double *ptrToB = &b; void *voidPtrToA = &a; void *voidPtrToB = &b;
指针的声明
用以下代码声明指针。其中,&运算符被称为取地址运算符,它返回变量在内存中的地址。
void指针可以指向任意类型的值。
int a; int *ptrToA = &a; double b; double *ptrToB = &b; void *voidPtrToA = &a; void *voidPtrToB = &b;
取地址运算符不能作用于常量或表达式,如 int *ptr = &2; 或者 int *ptr = &(a + b); 。因为他们在内存中并没有固定的地址。
空指针
空指针的值可以用 NULL (C和C++98的风格)或者 nullptr (C++11新标准)表示。他们的值都是0x0,表示指针不指向任何对象。
空指针不可以解引用,对其解引用会出现运行时错误(Runtime Error)。
NULL的定义:
//cite from <stddef.h> #ifndef __cplusplus #define NULL ((void *)0) // C语言 #else #define NULL 0 // C++ #endif
可以发现,C语言的NULL保证为 void* 类型,但C++的NULL仅为常量 0。在C++中使用NULL宏可能会导致函数重载错误。相比之下, C++11引入的 nullptr 始终保证其为指针类型。
在C++代码中,建议使用 nullptr。
知识点
指针赋值
使用指针时,假设 ptr 为一个指针。
ptr 的值为指针本身的值,是一个十六进制地址;
同类型的指针之间可以赋值,如 ptr1 = ptr2,赋值相当于改变了指针指向的对象。
间接访问
为了访问指针指向的值,我们使用 * 符号,这被称为解引用运算符:
*ptr 的值为指针指向的变量的值;
对 *ptr 的修改会作用到原对象上。
注意声明int *ptr = &a; 中的 * 并不是解引用运算符,它是类型声明的一部分。(非常非常非常重要的一句话,让我搞错了好长时间)
void类型
void* 指针可以指向任何类型的值。
void* 类型不可以解引用。
事实上,使用 void* 代表着你放弃了所有类型检查和类型安全性。因此,除非必要,不建议在C++代码中使用 void* 类型。此后我们会学到处理任意类型更强大、安全的工具:模板。
我们看一下以下代码的运行结果,加深一下理解:
int a = 233; int *ptrToA = &a; void *voidPtrToA = &a; cout << *ptrToA << endl; // 233 cout << ptrToA << endl; //一个十六进制数,表示内存中的位置。例如0x7ffd99314e64 *ptrToA = 466; cout << a << endl; // 466 *voidPtrToA = 699; // Compile Error: ‘void*’ is not a pointer-to-object type int c = 1, *ptrToC = &c; ptrToA = ptrToC; // 现在ptrToA指向了变量c reinterpret_cast
对于一个指向类型 A 的指针,我们可以将其转换成一个指向类型 B 的指针。此时,指针指向的位置没有变,只是对于内存中数据的解释方式变了。
转换的方式便是 B *ptr2 = reinterpret_cast<B *> ptr1;。
如以下代码,表示用一个 float 指针解释一个内存中的 int 变量:
int x = 1;
float *fp = reinterpret_cast<float *> &x;
要注意的是转换后的类型,它的有效长度不能比原来的类型更长。比如说 int 类型为 4 byte,double 类型为 8 byte。将一个指向 int 类型的指针转换成指向 double 类型的指针,这在语法上没有问题,但是如果解引用得到的指针,double 多出的 4 byte 的数据是无意义的。