完整目录、平台简介、安装环境及版本:参考《21天学C++ 概览》
八、指针
8.1 指针
8.1.1 指针是保存内存地址的变量
- 指针是变量;
- 该变量用来保存地址而不是值;
- 地址:计算机内存被划分成按顺序编号的内存单元,每个变量在内存中都位于独自的单元,即地址
- &为取存储变量的地址。
#include <iostream>
int main()
{
using namespace std;
unsigned short shortVar=5;
unsigned long longVar=65535;
long sVar = -65535;
cout << "shortVar:\t" << shortVar;
cout << "\tAddress of shortVar:\t";
cout << &shortVar << endl;
cout << "longVar:\t" << longVar;
cout << "\tAddress of longVar:\t" ;
cout << &longVar << endl;
cout << "sVar:\t\t" << sVar;
cout << "\tAddress of sVar:\t" ;
cout << &sVar << endl;
return 0;
}
执行结果如下:
shortVar: 5 Address of shortVar: 007FFB90
longVar: 65535 Address of longVar: 007FFB84
sVar: -65535 Address of sVar: 007FFB78
请按任意键继续. . .
8.1.2 声明指针变量
- int *pAge = 0;
- pAge是一个指向整型变量的指针,即pAge被声明为保存一个整型变量的地址;
- pAge初始化为0,即空指针;不用或未明确定义情况下应该赋值为空,否则成为失控指针。
- int howOld = 50;
- pAge = &howOld;
8.1.3 指针大小
- 对于win32,所有指针都是4个字节;
- 指针类型告诉编译器对于对象需要多上存储器去装载。
8.1.4 间接指针(*)
间接:使用指针保存的地址,访问变量的值;
普通变量可以直接访问他们自己的值,指针则提供了对变量值的间接访问,指针保存改变了的地址;
间接引用运算符在两种不同的情况下使用:
- 当声明指针时,*表示它是一个指针,而不是普通变量:如int *pAge = &howOld;
- 当指针被间接引用时,表示访问存储在指针中的内存地址处的值,而不是地址本身。
8.1.5 使用指针操作变量
把变量地址赋给指针后,就可以使用指针操作变量中的数据;
#include <iostream>
typedef unsigned short int USHORT;
int main()
{
using namespace std;
int myAge = 5; // a variable
int heAge = myAge;
int *pAge = &myAge; // a pointer
cout << myAge << endl;
cout << heAge << endl;
cout << *pAge << endl<<endl;
heAge = 9;
cout << myAge << endl;
cout << heAge << endl;
cout << *pAge << endl<<endl;
*pAge = 7;
cout << myAge << endl;
cout << heAge << endl;
cout << *pAge << endl<<endl;
cout<<&myAge<<endl;
cout<<&heAge<<endl;
cout<<pAge<<endl;
return 0;
}
执行结果如下:
5
5
5
5
9
5
7
9
7
00CFFEC0
00CFFEB4
00CFFEC0
请按任意键继续. . .
8.2 栈和自由存储区(堆)
8.2.1 栈
- 栈:局部变量和函数参数位于栈中,栈在函数返回时会自动清除;
- 代码空间:代码位于代码空间;
- 全局名字空间:全局变量位于全局区域,整个程序过程中访问不受任何控制;
- 寄存器:用于内部管理(如保存栈地址);
- 自由存储区:必须先申请,然后把它隐式地保存在一个指针中,程序结束前不会自动清除,需要手动释放;
8.2.2 new
- 用来为对象在自由存储区分配空间,根据对象类型确定分配多大的内存;
- new返回值为内存的地址,须赋给一个指针;
8.2.3 delete
new声明的空间需要手动的用delete释放,交还给自由存储区;
8.2.4 内存泄漏情形一
指针本身是个局部变量,当声明指针的函数返回时,指针的作用域也就结束了,被丢弃;
使用new分配的内存不会被自动释放,那么这块内存也就不能被其他数据使用,从而导致内存泄漏;
int main(){
int localVariable = 5;
int *pLocal= &localVariable;
int *pHeap = new int;
*pHeap = 7;
delete pHeap;
pHeap = new int;
*pHeap = 9;
delete pHeap;
return 0;
}
*pLocal是局部变量指针,无需释放;
*pHeap是堆指针,需要手动删除delete pHeap;
虽然程序退出后,pHeap会自动释放,但还是建议显示释放;
8.2.5 内存泄漏情形二
没有删除一个指针之前就对其重新赋值;
int *pointer = new int;
*pointer = 72;
pointer = new int; //保存值72的那块内存区域成为不可使用的空间,程序退出前无法释放(内存泄漏);
*pointer = 80;
程序new和delete应该配对使用,保证内存区域的申请和释放时非常重要的
int *pointer = new int;
*pointer = 70;
delete pointer;
pointer = new int;
*pointer = 80;
8.3 堆中类的使用
8.3.1 在自由存储区创建对象
Cat *pCat = new Cat;//调用默认构造函8.3.2
8.3.2 删除对象
delete pCat;//内存释放之前,调用析构函数;
#include <iostream>
using namespace std;
class SimpleCat
{
public:
SimpleCat();
~SimpleCat();
private:
int itsAge;
};
SimpleCat::SimpleCat()
{
cout << "Constructor called. " << endl;
itsAge = 1;
}
SimpleCat::~SimpleCat()
{
cout << "Destructor called. " << endl;
}
int main()
{
cout << "SimpleCat Frisky... " << endl;
SimpleCat Frisky;
cout << "SimpleCat *pRags = new SimpleCat..." << endl;
SimpleCat * pRags = new SimpleCat;
cout << "delete pRags... " << endl;
delete pRags;
cout << "Exiting, watch Frisky go... " << endl;
return 0;
}
执行结果如下:
SimpleCat Frisky...
Constructor called.
SimpleCat *pRags = new SimpleCat...
Constructor called.
delete pRags...
Destructor called.
Exiting, watch Frisky go...
Destructor called.
请按任意键继续. . .
8.3.3 访问数据成员
- (*Frisky).SetAge(5); //利用指针进行间接引用,然后使用成员运算符;
- Frisky ->SetAge(5); //成员指针运算符;
#include <iostream>
class SimpleCat
{
public:
SimpleCat() {itsAge = 2; }
~SimpleCat() {}
int GetAge() const { return itsAge; }
void SetAge(int age) { itsAge = age; }
private:
int itsAge;
};
int main()
{
using namespace std;
SimpleCat * Frisky = new SimpleCat;
cout << "Frisky is " << Frisky->GetAge() << " years old " << endl;
(*Frisky).SetAge(3);
cout << "Frisky is " << Frisky->GetAge() << " years old " << endl;
Frisky->SetAge(5);
cout << "Frisky is " << Frisky->GetAge() << " years old " << endl;
delete Frisky;
return 0;
}
执行结果如下:
Frisky is 2 years old
Frisky is 3 years old
Frisky is 5 years old
请按任意键继续. . .
8.3.4 堆中的成员数据
class SimpleCat{
public:
SimpleCat();
~SimpleCat();
private:
int * itsAge;
int * itsWeight;
};
SimpleCat::SimpleCat(){
itsAge = new int(2);
itsWeight = new int(5);
}
SimpleCat::~SimpleCat(){
delete itsAge;
delete itsWeight;
}
类的数据成员可以是指向堆中对象的一个指针,通过构造函数分配,析构函数释放;
SimpleCat *Frisky = new SimpleCat;//为数据成员分配空间
cout << Frisky->GetAge() << endl;
Frisky->SetAge(5) ;//隐藏,成员是否在堆中被隐藏
cout << Frisky->GetAge() << endl;//隐藏,成员是否在堆中被隐藏
delete Frisky;//释放数据成员空间
对象位于栈中,成员位于堆中
SimpleCat Frisky; //栈中两指针8个字节,堆中两成员8个字节
cout << Frisky.GetAge() << endl;
Frisky.SetAge(5);
cout << Frisky.GetAge() << endl;
8.4 this
每个成员函数都有一个隐藏的this指针,this指针指向每一个单独的对象;
this是一个指针,保存对象地址,编译器自动完成this的释放;
#include <iostream>
class Rectangle
{
public:
Rectangle();
~Rectangle();
void SetLength(int length)
{ this->itsLength = length; }
int GetLength() const
{ return this->itsLength; }
void SetWidth(int width)
{ itsWidth = width; }
int GetWidth() const
{ return itsWidth; }
private:
int itsLength;
int itsWidth;
};
Rectangle::Rectangle()
{
itsWidth = 5;
itsLength = 10;
}
Rectangle::~Rectangle()
{}
int main()
{
using namespace std;
Rectangle theRect;
cout << "theRect is " << theRect.GetLength()
<< " feet long." << endl;
cout << "theRect is " << theRect.GetWidth()
<< " feet wide." << endl;
theRect.SetLength(20);
theRect.SetWidth(10);
cout << "theRect is " << theRect.GetLength()
<< " feet long." << endl;
cout << "theRect is " << theRect.GetWidth()
<< " feet wide. " << endl;
return 0;
}
执行结果如下:
theRect is 10 feet long.
theRect is 5 feet wide.
theRect is 20 feet long.
theRect is 10 feet wide.
请按任意键继续. . .
8.5 迷途指针
- 也叫失控指针或悬浮指针,即编译器释放了内存,但指针本身依然存在;
- 使用delete释放空间后,没有将指针设置为空,而试图再使用该指针,将引起不可预料的结果,甚至程序崩溃;
- 为了安全起见,在删除一个指针后,将它设置为空值0。
typedef unsigned short int USHORT;
#include <iostream>
int main()
{
USHORT * pInt = new USHORT;
*pInt = 10;
std::cout << "*pInt: " << *pInt << std::endl;
delete pInt;
std::cout<<sizeof(USHORT);
long * pLong = new long;
*pLong = 90000;
std::cout << "*pLong: " << *pLong << std::endl;
*pInt = 20; // uh oh, this was deleted!
std::cout << "*pInt: " << *pInt << std::endl;
std::cout << "*pLong: " << *pLong << std::endl;
delete pLong;
return 0;
}
执行结果如下:
*pInt: 10
2*pLong: 90000
*pInt: 20
*pLong: 90000
请按任意键继续. . .
pint指向的内存值10:00 00 00 1A
释放后,继续分给pLong=90000:00 01 5F 90
接着继续给pint指向赋值20:00 00 00 14
//short为2字节,进覆盖前面两个字节
覆盖后,内存值为:00 01 00 14即65556
如果pint指向类型是int,则会全覆盖,最后输出20
8.6 const 指针
- const int * pOne; //指向的值不能改变;(指向的内存值不能变)
- int * const pTwo; //指向的值可以改变,但是不能指向其他变量;(保存值的地址不能变)
- const int * const pThree; //指向的值不能变,指针也不能指向其他变量;
#include <iostream>
using namespace std;
class Rectangle
{
public:
Rectangle();
~Rectangle();
void SetLength(int length) { itsLength = length; }
int GetLength() const { return itsLength; }
void SetWidth(int width) { itsWidth = width; }
int GetWidth() const { return itsWidth; }
private:
int itsLength;
int itsWidth;
};
Rectangle::Rectangle()
{
itsWidth = 5;
itsLength = 10;
}
Rectangle::~Rectangle()
{}
int main()
{
Rectangle* pRect = new Rectangle;
const Rectangle * pConstRect = new Rectangle;
Rectangle * const pConstPtr = new Rectangle;
cout << "pRect width: " << pRect->GetWidth() << " feet" << endl;
cout << "pConstRect width: " << pConstRect->GetWidth() << " feet" << endl;
cout << "pConstPtr width: " << pConstPtr->GetWidth() << " feet" << endl;
pRect->SetWidth(10);
// pConstRect->SetWidth(10);
pConstPtr->SetWidth(10);
//pConstPtr = new Rectangle;
cout << "pRect width: " << pRect->GetWidth() << " feet\n";
cout << "pConstRect width: " << pConstRect->GetWidth() << " feet\n";
cout << "pConstPtr width: " << pConstPtr->GetWidth() << " feet\n";
return 0;
}
执行结果如下:
pRect width: 5 feet
pConstRect width: 5 feet
pConstPtr width: 5 feet
pRect width: 10 feet
pConstRect width: 5 feet
pConstPtr width: 10 feet
请按任意键继续. . .
8.7 指针主要用途
- 处理自由存储区(堆)的数据;
- 访问类的成员数据和函数;
- 通过引用的方式向函数传递变量。