int *p;
定义了指针变量p
,这个变量储存了一个地址,这个地址对应的变量是int
类型的。&
是取地址符,int *p = &x;
就定义了一个指向x
的指针。指向相同类型变量的指针之间可以相互赋值(不同类的不行,指向的地址长度可能不同)。在程序中,可以把*p
看作x
,例如*p = y+1;
表示把y+1
赋值给x
。
const int *p;
表示p
指向一个const int
,因此*p
不能修改,但p
可以修改;int *const p = &x;
(定义时就要给出初值)表示p
只能指向x
,*p
可以修改,但p
不能修改。如果是const int *const p = &x;
就都不能修改。
对于暂时没有使用的指针应当赋为nullptr
(这是C++11的新用法,它避免了NULL
隐式转换为整型0
的问题,它不能被隐式转换为整型)
指针可以进行加减运算,它基于指向的变量类型的字节数。假如p
指向整型,那么p+1
实际上对应着地址+4。
C++的数组名是一个指针常量。a[i]
本质上就是*(a+i)
。如果执行p=i
,那么也可以用p[i]
来访问*(p+i)
。定义数组与定义指针变量仅仅在内存分配上产生差别。
可以用指针申请动态内存空间。对于指针变量int *p
,执行p = new int;
。申请到的内存空间属于“堆”,我们操作得到这片内存的首地址。在申请的时候可以顺便赋初值,p = new int(10);
。也可以申请一个动态变量数组p = new int [10];
(可以访问p[0]
到p[9]
)。可以顺便赋初值p = new int[5]{1,2,3,4,5};
。数组的长度可以是变量而不一定要是常量。
动态内存空间必须手动回收。回收单个动态变量,执行delete p;
。(p
是指向它的指针变量)。回收数组,执行delete [] p;
。回收之后,指针变量依然指向那片空间,但是无法访问。
堆空间有限,在new
之前最好先确认申请成功。先写p = new int;
。如果申请失败,p
的值会被赋为nullptr
。
对于一个指向字符的指针变量s
,可以执行s="abcde"
,右侧是一个字符串常量,它在内存中与静态变量储存在一起。可以访问s[3]
,但是不能修改s[3]
。
C++函数参数只是被参数值初始化了的局部变量。然而,如果参数是指针,那么操作指针就可以改变变量的值了。因此,也可以把指针参数传入函数专门用来储存答案,这样一个函数就可以“有多个返回值”。函数中的数组参数void f(int a[])
实际上只是一个指针,等价于void f(int *a)
,在初始化的时候将对应的指针赋给了a
而已。函数的返回值也可以是一个指针,比如char *f(int a)
。
引用是变量的别名,定义时必须赋值int &j = i
,之后在代码中j
就表示i
。本质上引用是一个隐式的指针,可以让我们不用写*
。但引用变量的绑定是永久的,不能再执行j = &c
。 引用类型可以引用引用类型,例如int &k = j
,那么k,j,i
就都是同一个东西。也可以引用常量const int &a = 1
,实质是系统生成一个值为1的临时变量(不能修改)并让a
作为这个变量的别名(一个隐式指针),也可以执行const int &a = b
,那么a
就不能被重新赋值,但普通变量b
可以被赋值。
C++引入“引用”的目的主要是作为函数的参数。在swap中定义void swap(int &a, int &b)
,那么调用swap(x,y)
相当于给局部变量赋了初值int &a = x, &b = y
,那么a,b
就作为隐式指针指向了x,y
,因此a,b
就称为实参了。这和void swap(int *a, int *b)
和swap(&x, &y)
是等价的,但是写起来更方便。
函数的返回值可以是引用,这相当于返回了一个变量,可以作为左值。假如有函数int &index(int j){ return a[j]; }
那么就可以调用index(2) = 25
。返回的引用变量不能是局部变量或者表达式,因为必须确保在调用时这个变量仍然存在。
可以定义由指针变量组成的“指针数组”,char *s[20]
可以用来表示一组字符串。而由于数组名本身是一个指针,所以s
本身是一个指向指针变量的指针(多级指针)。int x=15, *p = &x, **q = &p;
,q
就是一个二级指针。
C++不能申请动态的二维数组。然而,可以先申请动态的指针数组int **a; a = new int *[3]; ... a[i] = new int [3]
。回收时执行delete [] a[i]; delete [] a;
。
指针可以指向一个函数,double (*p)(int, int);
定义了一个指向函数的指针,这个函数有两个int
参数,返回类型是double
。(*p)
的括号不能省略,不然就被当作函数原型了。