指针
内存空间的访问方式
如何利用内存单元存储数据?
-
- 通过变量名
- 通过地址
具有静态生存期的变量——在程序运行之前,就已经分配了内存空间;
具有动态生存期的变量——在程序运行时,遇到变量声明语句时被分配内存空间的。
通过地址访问内存单元,why?
-
- 在不同函数之间传送大量数据时,如果不传递变量的值,只传递变量的地址,就会减小系统开销,提高效率;
- 如果是动态分配的内存单元,则根本就没有名称,这时只能通过地址访问。
指针变量的声明
指针变量——用于存放内存单元地址
指针——可以指向指针
程序中声明一个变量时:
-
- 声明了变量需要的内存空间;
- 限定了对变量可以进行的运算及其运算规则;
地址运算符
*
-
- 声明语句中,在被声明的变量之前时,表示声明的是指针;
- 执行语句中或声明语句的初始化表达式中,表示访问指针所指对象的内容。
&
-
- 声明语句中,在被声明的变量左边时,表示声明的是引用;
- 执行语句中或给变量赋初值在等号右边,表示取对象的地址。
指针的赋值
关于指针的类型,应注意:
- 声明指向常量的指针
不能通过指针改变所指对象的值,但指针本身可改变
int a;
const int *p1=&a;
int const *p3=&a;
- 声明指针类型的常量
数组名——不能被赋值的指针(指针常量)
指针本身的值不可被改变
int a;
int *const p2=&a;
- void类型指针——可以存储任何类型的对象地址
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 using namespace std; 3 4 int main() 5 { 6 int i; 7 8 int *ptr = &i;//i地址赋给ptr 9 i = 10; 10 11 cout << "i=" << i << endl;//直接访问 12 cout << "ptr内容为:" << *ptr << endl;//间接访问 13 14 cout << "ptr:" << ptr << endl; 15 cout << "i的地址为:" << &i << endl << endl; 16 17 18 int a; 19 const int *p1 = &a;//指向常量的指针,指针内容不可变,指针本身可以变 20 cout << "p1:" << p1 << endl; 21 int b; 22 p1 = &b; 23 //*p1 = 1; 24 cout << "p1:" << p1 << endl << endl; 25 26 int *const p2 = &a;//指针类型的常量,指针本身的值不能改变 27 //p2 = &b; 28 cout << "p2:" << p2 << endl << endl; 29 30 int const *p3 = &a; 31 cout << "p3:" << p3 << endl; 32 p3 = &b; 33 cout << "p3:" << p3 << endl; 34 35 return 0; 36 }
指针运算
指针p1和整数n1
p1+n1 表示p1当前所指位置后方第n1个数的地址
p1++ 表示指针当前所指位置下一个数据的地址
p1--
*(p1+n1) 表示p1当前所指位置后方第n1个数的内容
p1[n1] 同上
空指针用NULL或0表示,即一个不指向任何有效地址的指针(NULL是在很多头文件中都有定义的宏,被定义为0);
用指针处理数组元素
数组名——数组存储的首地址
输出数组元素的三种方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 using namespace std; 3 int main() 4 { 5 int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 6 7 cout << "数组元素:" << endl; 8 for (int i = 0; i < 10; i++) 9 cout << a[i] << endl; 10 11 cout << "数组元素:" << endl; 12 for (int i = 0; i < 10; i++) 13 cout << *(a + i) << endl; 14 15 cout << "数组元素:" << endl; 16 for (int *pp = a; pp < (a + 10); pp++) 17 cout << *pp << endl; 18 return 0; 19 }
指针数组
由于指针数组的每个元素都是指针,必须先赋值后引用;
指针数组与二维数组的区别:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 using namespace std; 3 4 int main() 5 { 6 int array[3][3] = { { 11, 12, 13 }, { 21, 22, 23 }, { 31, 32, 33 } }; 7 8 cout << "数组元素" << endl; 9 for (int i = 0; i < 3; i++) 10 { 11 for (int j = 0; j < 3; j++) 12 { 13 cout << array[i][j] << " "; 14 } 15 cout << endl; 16 } 17 18 cout << "数组元素" << endl; 19 for (int i = 0; i < 3; i++) 20 { 21 for (int j = 0; j < 3; j++) 22 { 23 cout << *(array[i] + j) << " "; 24 } 25 cout << endl; 26 } 27 28 cout << "数组元素" << endl; 29 for (int i = 0; i < 3; i++) 30 { 31 for (int j = 0; j < 3; j++) 32 { 33 cout << *(*(array + i) + j) << " "; 34 } 35 cout << endl; 36 } 37 38 return 0; 39 }
指针作为函数参数
作用
- 使实参与形参指向共同的内存空间,以达到参数双向传递的目的。即通过在被调函数中直接处理主函数中的数据,而将函数的处理结果返回其调用者;
- 减少函数调用时数据传递的开销;
- 通过指向函数的指针传递函数代码的首地址。
读入浮点数,将整数部分和小数部分分别输出。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 using namespace std; 3 void splitFLoat(float x, int *intPart, float *floatPart); 4 5 int main() 6 { 7 float x; 8 cout << "请输入x:"; 9 cin >> x; 10 int intPart; 11 float floatPart; 12 splitFLoat(x, &intPart, &floatPart); 13 cout << "intPart: " << intPart << endl; 14 cout << "floatPart: " << floatPart << endl; 15 return 0; 16 } 17 18 19 void splitFLoat(float x, int *intPart, float *floatPart) 20 { 21 *intPart = static_cast<int>(x); 22 *floatPart = x - *intPart; 23 }
指针函数(函数返回值是指针类型——指针型函数)
目的:
在函数结束时,把大量数据从被调函数返回到主调函数中;
而通常非指针函数调用结束之后,只能返回一个变量或对象。
定义形式:
数据类型 *函数名(参数表)
{
函数体
}
函数名和“*”标识了一个指针型的函数
函数指针(指向函数的指针——用来存放代码首地址的变量)
在程序运行时,不仅数据要占据内存空间,执行程序的代码也被调入内存,并占据一定的内存空间。
每个函数的函数名表示函数的代码在内存中的起始地址,即:函数名(参数表)——函数代码首地址(参数表)
通过赋值语句使指针分别指向三个函数,然后通过函数指针实现对函数的调用。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 using namespace std; 3 4 void printStuff(float) 5 { 6 cout << "stuff" << endl; 7 } 8 9 void printMessage(float data) 10 { 11 cout << "Message" << data << endl; 12 } 13 14 void printFloat(float data) 15 { 16 cout << "float" << data << endl; 17 } 18 19 const float PI = 3.14; 20 const float TWO_PI = 2.0f*PI; 21 22 int main() 23 { 24 void(*functionPointer)(float); 25 26 printStuff(PI); 27 functionPointer = printStuff; 28 functionPointer(PI); 29 30 functionPointer = printMessage; 31 functionPointer(PI); 32 functionPointer(13.21); 33 34 functionPointer = printFloat; 35 functionPointer(PI); 36 printFloat(PI); 37 38 return 0; 39 }
对象指针——存放对象地址的变量
对象所占据的内存空间只用来存放对象的数据成员,函数成员不在每一个对象中存储副本。
对象指针名->成员名
(*对象指针名).成员名(等价于上式)
使用指针访问Point类的成员:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 using namespace std; 3 4 class Point 5 { 6 public: 7 Point(int x = 0, int y = 0) :x(x), y(y) 8 { 9 } 10 11 int getX() 12 { 13 return x; 14 } 15 16 int getY() 17 { 18 return y; 19 } 20 21 private: 22 int x, y; 23 }; 24 25 int main() 26 { 27 Point a(4, 5); 28 Point *p1 = &a; 29 30 cout << p1->getX() << endl; 31 cout << (*p1).getX() << endl; 32 33 cout << a.getX() << endl; 34 return 0; 35 }
对象指针在使用之前,一定要先进行初始化。
- this指针——隐含于每一个类的非静态成员函数中的特殊指针(包括构造函数和析构函数),它用于指向正在被成员函数操作的对象。
this是一个指针常量,对于常成员函数,this同时又是一个指向常量的指针。在成员函数中,可以使用*this来标识正在调用该函数的对象。
- 指向类的非静态成员的指针
指向对象成员的指针使用前也要先声明,再赋值,后引用。
声明指针语句的一般形式:
类型说明符 类名::*指针名;
类型说明符 (类名::*指针名)(参数表);
对数据成员指针赋值的一般语法形式:
指针名=&类名::数据成员名;
访问数据成员或成员函数时,可以通过将对象在内存中的起始地址与成员指针中存放的相对偏移结合实现:
对象名.*类成员指针名 对象名.*类成员指针名(参数表)
对象指针名->*类成员指针名 对象指针名->*类成员指针名(参数表)
成员函数指针在声明之后要对其赋值:
指针名=&类名::函数成员名
访问对象公有成员函数的不同方式:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 using namespace std; 3 4 class Point 5 { 6 public: 7 Point(int x = 0, int y = 0) :x(x), y(y){} 8 int getX() 9 { 10 return x; 11 } 12 int getY() 13 { 14 return y; 15 } 16 private: 17 int x, y; 18 }; 19 20 int main() 21 { 22 Point a(3, 32); 23 Point *p1 = &a; 24 int (Point::*funPtr)() = &Point::getX; 25 26 //访问对象的公有成员函数的不同方式 27 cout << (a.*funPtr)() << endl; //成员函数指针+对象名 28 cout << (p1->*funPtr)() << endl;//成员函数指针+对象指针 29 cout << a.getX() << endl; //对象名 30 cout << p1->getX() << endl; //对象指针 31 32 return 0; 33 }
- 指向类的静态成员指针
对类的静态成员的访问是不依赖于对象的,因此可以用普通的指针来指向和访问静态成员。
通过指针访问类的静态数据成员
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 using namespace std; 3 4 class Point{ 5 public: 6 7 Point(int x = 0, int y = 0) :x(x), y(y) 8 { 9 count++; 10 } 11 12 Point(const Point &p) :x(p.x), y(p.y) 13 { 14 count++; 15 } 16 17 ~Point() 18 { 19 count--; 20 } 21 22 int getX() 23 { 24 return x; 25 } 26 27 int getY() 28 { 29 return y; 30 } 31 32 static int count; 33 34 private: 35 int x, y; 36 }; 37 38 int Point::count = 0; 39 40 int main() 41 { 42 int *ptr = &Point::count; 43 Point a(3, 4); 44 cout << "a:" << a.getX() << "," << a.getY() << endl; 45 cout << "count=" << *ptr << endl; 46 47 Point b(a); 48 cout << "b:" << b.getX() << "," << b.getY() << endl; 49 cout << "count=" << *ptr << endl; 50 51 return 0; 52 }
通过指针访问类的静态函数成员
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 using namespace std; 3 class Point{ 4 public: 5 6 Point(int x = 0, int y = 0) :x(x), y(y) 7 { 8 count++; 9 } 10 11 Point(const Point &p) :x(p.x), y(p.y) 12 { 13 count++; 14 } 15 16 ~Point() 17 { 18 count--; 19 } 20 21 int getX() 22 { 23 return x; 24 } 25 26 int getY() 27 { 28 return y; 29 } 30 31 static void showCount() 32 { 33 cout << "current count=" << count << endl; 34 } 35 36 private: 37 int x, y; 38 static int count; 39 40 }; 41 42 int Point::count = 0; 43 44 int main() 45 { 46 void(*funPtr)() = Point::showCount; 47 48 Point a(7, 8); 49 cout << "a:" << a.getX() << "," << a.getY() << endl; 50 funPtr(); 51 52 Point b(a); 53 cout << "b:" << b.getX() << "," << b.getY() << endl; 54 funPtr(); 55 }