21天学C++(八)指针

完整目录、平台简介、安装环境及版本:参考《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 指针主要用途

  • 处理自由存储区(堆)的数据;
  • 访问类的成员数据和函数;
  • 通过引用的方式向函数传递变量。

发表回复