变量是可存储一个值的对象:整型变量存储一个数字,字符变量存储一个字母,而指针是存储内存地址的变量。ios
计算机内存是存储变量值的地方。根据约定,计算机内存被划分红按顺序编号的内存单元,每一个内存单元都有对应的地址。内存中,无论其类型是什么,每一个变量都位于特定的地址处。c++
内存编址方案随计算机而异。一般,程序员无须知道变量地址,若是想要获取地址信息,可以使用地址运算符&程序员
程序清单10.1 Address.cpp安全
#include<iostream> int main() { unsigned short shortVar = 5; unsigned long longVar = 65535; long sVar = -65535; std::cout<<"shortVar:\t"<<shortVar; std::cout<<"\tAddress of shortVar:\t"<<&shortVar<<"\n"; std::cout<<"longVar:\t"<<longVar; std::cout<<"\tAddress of longVar:\t"<<&longVar<<"\n"; std::cout<<"sVar:\t"<<sVar; std::cout<<"\tAddress of sVar:\t"<<&sVar<<"\n"; }
(地址默认以16进制表示法输出的)数据结构
您运行该程序时,变量的地址将不一样,由于这取决于内存中存储的其余内容以及可用的内存有多少。app
在指针中存储地址函数
每一个变量都有地址,即便不知道变量的具体地址,也可将该地址存储在指针变量中。操作系统
int howOld = 50; int* pAge = nullptr;//初始化一个int型空指针变量,这样能更明显看出来pAge类型是int*,但c/c++的标准写法是int *pAge pAge = &howOld;//将howOld的地址取出来放入指针变量pAge中
间接运算符指针
间接运算符(*)又被称为解引用运算符。对指针解除引用时,将获取指针存储的地址处的值。code
int howOld = 50; int* pAge = &howOld; int yourAge; yourAge = *pAge;//yourAge的值变成了50 *pAge = 10;//howOld的值变成了10,而yourAge的值仍是50指针pAge前面的间接运算符(*)表示“存储在......处的值”。这条赋值语句的意思是,从pAge指向的地址处获取值,并将其赋给yourAge。看待这条语句的另外一种方式是,不影响指针,而是影响指针指向的内容(好比上面最后一条语句)。
使用指针操做数据(其实上面那个例子就是)
程序清单10.2 Pointer.cpp
#include <iostream> int main() { int myAge; int *pAge = nullptr; myAge = 5; pAge = &myAge; std::cout << "myAge: " << myAge << "\n"; std::cout << "*pAge: " << *pAge << "\n\n"; std::cout << "*pAge = 7\n"; *pAge = 7; std::cout << "myAge: " << myAge << "\n"; std::cout << "*pAge: " << *pAge << "\n\n"; std::cout << "myAge = 9\n"; myAge = 9; std::cout << "myAge: " << myAge << "\n"; std::cout << "*pAge: " << *pAge << "\n"; }
查看存储在指针中的地址:
程序清单10.3 PointerCheck.cpp
#include <iostream> int main() { unsigned short int myAge = 5, yourAge = 10; unsigned short int *pAge = &myAge; std::cout << "pAge: " << pAge << "\n"; std::cout << "*pAge: " << *pAge << "\n"; pAge = &yourAge; std::cout << "after reassign the pAge point to yourAge : " << "\n"; std::cout << "pAge: " << pAge << "\n"; std::cout << "*pAge: " << *pAge << "\n"; return 0; }
为什么使用指针
熟悉指针的语法后,即可将其用于其余用途了,指针最长用于完成以下三项任务:
- 管理堆中的数据;
- 访问类的成员数据和成员函数;
- 按引用将变量传递给函数
(这部分其实若是有一点数据结构或者操做系统基础更好)
程序员一般须要处理下述五个内存区域
- 全局名称空间
- 堆
- 寄存器
- 代码空间
- 栈
局部变量和函数参数存储在栈中,代码固然在代码空间中,而全局变量在全局名称空间中。寄存器用于内存管理,如跟踪栈顶和指令指针,几乎余下的全部内存都分配给了堆,堆有时也被称为自由存储区。
局部变量的局限性是不会持久化,函数返回时,局部变量将被丢弃。全局变量解决了这种问题,但代价是在整个程序中都能访问它,这致使代码容易出现bug,难以理解与维护。将数据放在堆中可解决这两个问题。
每当函数返回时,都会清理栈(实际上,开始调用函数时,栈空间进行压栈操做;函数调用完时,栈空间进行出栈操做)。此时,全部的局部变量都不在做用域内,从而从栈中删除。只有到程序结束后才会清理堆,所以使用完预留的内存后,您须要负责将其释放(手动GC)。让再也不须要的信息留在堆中称为内存泄露(垃圾滞留)。
堆的优势在于,在显示释放前,您预留的内存始终可用。若是在函数中预留堆中的内存,在函数返回后,该内存仍可用。
以这种方式(而不是全局变量)访问内存的优势是,只有有权访问指针的函数才能访问它指向的数据。这提供了控制严密的数据接口,消除了函数意外修改数据的问题。
关键字new
在c++中,使用new关键字分配堆中的内存,并在其后指定要为之分配内存的对象的类型,让编译器知道须要多少内存。好比
new int
分配4字节内存。关键字new返回一个内存地址,必须将其赋给指针。
int *pPointer = new int;//指针pPointer将指向堆中的一个int变量 *pPointer = 72;//将72赋值给pPointer指向的堆内存变量
关键字delete
使用好了分配的内存区域后,必须对指针调用delete,将内存归还给堆空间。
delete pPointer;对指针调用delete时,将释放它指向的内存。若是再次对该指针调用delete,就会致使程序崩溃(delete野指针)。删除指针时,应将其设置为nullptr,对空指针调用delete是安全的。
Animal *pDog = new Animal; delete pDog;//释放内存 pDog = nullptr;//设置空指针 delete pDog;//安全行为
程序清单10.4 Heap.cpp
#include <iostream> int main() { int localVariable = 5; int *pLocal = &localVariable; int *pHeap = new int; if (pHeap == nullptr) { std::cout << "Error! No memory for pHeap!!"; return 1; } *pHeap = 7; std::cout << "localVariable: " << localVariable << std::endl; std::cout << "*pLocal: " << *pLocal << std::endl; std::cout << "*pHeap: " << *pHeap << std::endl; delete pHeap; //此处只是释放了堆中new分配的内存,并无删除指针,因此下面能够接着用。 pHeap = new int; if (pHeap == nullptr) { std::cout << "Error! No memory for pHeap!!"; return 1; } *pHeap = 9; std::cout << "*pHeap: " << *pHeap << std::endl; delete pHeap; //再次释放new出来的内存 return 0; }
另外一种可能无心间致使内存泄露的情形是,没有释放指针指向的内存就给它从新赋值。
int *pPointer = new int; *pPointer = 72; pPointer = new int; *pPointer = 50;//指针变量指向了一个新new出来的存有50的int型变量,可是以前那个存有72的堆内存变量还没被释放,也就形成了内存泄露上述代码应该修改为这样:
int *pPointer = new int; *pPointer = 72; delete pPointer; pPointer = new int; *pPointer = 50;
也就是说一个不存在内存泄露的c++程序至少其new与delete是成对的,或者说是数量相等的。(这可苦了c艹程序员咯!)
空指针常量:
在较早的c++版本中,使用0或者NULL来设置指针为空值。
int *pBuffer = 0; int *pBuffer = NULL;
可是其实由于定义NULL的语句是一个预处理宏:
#define NULL 0
因此其实NULL和0一个意思,上面两句也是一个意思。
可是当某个函数进行了重载,参数有指针类型也有int型的时候,传了一个值为0的空值指针进去,这个时候会出现二义性:
void displayBuffer(char*);//这个函数的参数是char型指针变量 void displayBuffer(int);
若是将空指针做为参数去调用,那么会调用displayBuffer(int),这个时候就会形成运行与预期不一样。
因此才应该使用关键字nullptr
int *pBuffer = nullptr;
程序清单10.5 Swapper.cpp
#include <iostream> int main() { int value1 = 12500; int value2 = 1700; int *pointer2 = nullptr; pointer2 = &value2; value1 = *pointer2; pointer2 = 0; std::cout << "value = " << value1 << "\n"; return 0; }