C++ 指针
整理自《面向对象程序设计》
指针是 C++ 语言中的重要概念,也是 C++ 语言的重要特色。使用指针,可以使程序更加简洁、紧凑和高效。
计算机硬件系统的内存存储器中,拥有大量的存储单元。为了方便管理,必须为每一个存储单元编号,这个编号就是存储单元的“地址”。每个存储单元都拥有唯一的地址。由于根据存储单元的编号或地址就可以找到所需内存单元,所以通常也把这个地址称为指针。
1.指针变量的定义
在 C++ 语言中,允许用一个变量来存放指针,这种变量称为指针变量。因此,一个指针变量的值就是某个存储单元的地址或称为某存储单元的指针。(个人感觉可以把所有的 “指针” 二字替换成 “地址” 来理解)
其一般定义形式为:
类型说明符 * 变量名;
其中,* 表示这是一个指针变量,变量名即定义指针变量名,类型说明符表示本指针变量所指向的变量的数据类型。
例如: int * p; 表示 p 是一个指针变量,它的值是某个整型变量的地址,或者说 p 指向一个整型变量。至于 p 究竟指向哪个整型变量,应由给 p 赋值的地址来决定。
应该注意的是,一个指针变量只能指向同类型的变量,如 p 只能指向整型变量,不能时而指向一个浮点变量, 时而又指向一个字符变量。
2.两个重要运算符
(1)取地址运算符 &
取地址运算符&是单目运算符,其结合性为自右至左,其功能是得到变量在存储器中的首地址。操作对象是变量。
(2)取内容运算符 *(指针运算符)
取内容运算符*是单目运算符,其结合性为自右至左,用来引用指针变量所指向的变量,操作对象是指针。在运算符之后跟的变量必须是指针变量。需要注意的是指针运算符*和指针变量说明中的指针说明符* 不是一回事。在指针变量说明中,* 是类型说明符,表示其后的变量是指针类型。而表达式中出现的 * 则是一个运算符用以表示指针变量所指的变量。
3.指针变量的赋值
C++ 语言中提供了地址运算符 & 来表示变量的地址。其一般形式为 & 变量名;如 &a 表示变量 a 的地址,&b 表示变量 b 的地址。变量本身必须预先说明。设有指向整型变量的指针变量 p ,如果要把整型变量 a 的地址赋给 p 可以有以下3种方式。
1.指针变量初始化的方法
int a;
int *p = &a;
2.赋值语句的方法
int a;
int *p;
p = &a;
3.把一个指针变量的值赋给指向相同类型变量的另一个指针变量
int a,*pa = &a,*pb;
pb=pa; //把 a 的地址赋给指针变量 pb,由于 pa,pb均为指向整型变量的指针变量,因此可以相互赋值
4.指针变量作函数参数
形参与实参
(1)形参
函数定义时使用的参数称为“形式参数”,简称“形参”,其作用是告知使用者在使用该函数时需要传递的数据的类型与个数。此时的参数只有类型和个数的概念,无实际值。
(2)实参
在调用某一函数时使用的参数称为“实际参数”,简称“实参”,其作用是将所需要的数据传递给相应的形参。此时实参要按照形参的类型和个数对应排列。实参是具体的数据,调用时会一一对应地传递给相应的形参。
指针变量既可以作为函数的形参,也可以作为函数的实参。指针变量作实参时,与普通变量一样,也是“值传递”,即将指针变量的值(一个地址)传递给被调用函数的形参(必须是一个指针变量)。
被调用函数不能改变实参指针变量的值(一个地址),但可以改变实参指针变量所指向的变量的值。为了能使用被调函数改变的变量值,应该使用指针作函数的实参(即转递地址)。其机制为:在执行被调函数时,使形参指针变量所指向的变量的值发生变化;函数调用结束后,通过不变的实参指针将变化的值保留下来。
5.引用
引用的使用是 C++ 语言不同于 C 语言的一个重要新特性,也是对 C 的一个重要扩充。
引用就是变量的一个别名,引用的声明就是给某一个变量起别名,而且要求声明引用时,必须同时对其进行初始化。引用的声明格式如下:
类型标识符 & 引用名 = 目标变量名
例如:
int a; int &are = a; //定义引用are,并且指明它是变量 a 的引用
其中,类型标识符是指向目标变量的类型;& 在此用来标识当前是引用,而不是求地址运算符;声明了变量的一个引用,表示该引用名是目标变量名的一个别名,并不是新定义了一个变量;引用本身也不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。
声明一个引用,等于目标变量多了一个名字,可以通过这个新的别名,也就是引用名来对变量进行操作,其效果和直接使用变量名进行操作完全一样。例如:
are = 3; 等价于 a = 3;
6.示例
这里只给出了简要的注释,想要详细理解这些代码,可以浏览文末给出的参考博客。
#include<iostream> using namespace std; int main() { int array[20]; int *ptr = array; for (int i = 0; i<20; i++) array[i] = 0; for (int i = 0; i<20; i++) { (*ptr) += i; ptr++; } for (int i = 0; i<20; i++) cout << array[i] << " "; cout << endl; char a='a'; char *p=&a; char **ptr_ = &p;//&p的结果是个指针,该指针的类型是p的类型加个*,在这里是int**。该指针所指向的类型是p的类型,这里是int*。该指针所指向的地址就是指针p自己的地址。 cout << "双重指针指向的地址指向的值为:"<<**ptr_ << endl; char *arr[3] = { "123","abc","ABC" }; char **parr = arr;//如果把arr看作指针的话,arr也是指针表达式 char *str; str = *parr;//*parr是指针表达式,该指针的类型是parr的类型减个*,在这里是char*. cout << "单指针指向的值为:"; for(int i=0;i<12;i++) cout << *(str+i) ;//输出的是起始地址开始的前十二个char型单元 cout << endl; getchar(); return 0; }
#include<iostream> #include<cstring> #include<string> #include<cstdio> using namespace std; int main(){ char *str[2]={ "Hello,this is a sample! ", "Hi,good morning." }; char s[80]; cout<<"1:"<<str[0]<<str[1]<<endl; int len1=strlen(str[0]); strcpy(s,*str);//strcpy赋值的两种方法 strcpy(s+len1,str[1]); printf("2: %s\n",s); cout<<"sizeof(int(*)[10])="<<sizeof(int(*)[10])<<endl; //sizeof(指针名称)测出的究竟是指针自身类型的大小呢还是指针所指向的类型的大小?答案是前者。 cout<<"sizeof(int [10])="<<sizeof(int [10])<<endl; getchar(); return 0; }
#include<iostream> using namespace std; int main(){ struct MyStruct { int a; int b; int c; }; MyStruct ss={20,30,40};//声明了结构对象ss,并把ss的三个成员初始化为20,30和40。 MyStruct *ptr=&ss;//声明了一个指向结构对象ss的指针。它的类型是MyStruct*,它指向的类型是MyStruct。 int *pstr=(int*)&ss;//声明了一个指向结构对象ss的指针。但是它的类型和它指向的类型和ptr是不同的。 cout<<"访问结构类型对象的指针:"<<ptr->a<<" "<<ptr->b<<" "<<ptr->c<<endl; cout<<"访问整型对象的指针:"<<*pstr<<" "<<*(pstr+1)<<" "<<*(pstr+2)<<endl; /*这样使用pstr来访问结构成员是不正规的。所有的C/C++编译器在排列数组的单元时,总是把各个数组单元存放在连续的存储区里,单元和单元之间没有空隙。 但在存放结构对象的各个成员时,在某种编译环境下,可能会需要字对齐或双字对齐或者是别的什么对齐,需要在相邻两个成员之间加若干个“填充字节”, 这就导致各个成员之间可能会有若干个字节的空隙。*/ getchar(); return 0; }
文字内容整理自《面向对象程序设计》
示例中的代码参考自:https://www.cnblogs.com/ggjucheng/archive/2011/12/13/2286391.html,这篇博客写的比较全面,建议阅读。