指针(Pointer),从英文字面上理解就是一个指向某一物件的东西,在程序中就是指向数据的地址Address。
1. 指针的声明
在变量类型名和变量名之间加上星号 *
,星号尽量靠近变量名,如下列代码中的 *intPtr1
和*intPtr2
,不然很容易在声明多个指针的时候遗漏后面变量名的星号,如 intPtr4
,仅仅声明了一个整形变量。
int *intPtr1, *intPtr2;
int* intPtr3, intPtr4;
在声明指针时一定要初始化指针,如果没有初始化,他可能指向一个未知的地址。
2. 指针的类型
指针的类型表示了指针指向的地址所存储的数据类型,如果需要将 int *
指针转换为 float *
指针,那么程序也只是将数据重新解读为浮点型数据。
void *
指针只是代表一个地址,不知道它所指向的数据类型,但可以重新定义它所指向的数据类型。
3. 指针的基本操作
解引用*
获取指针所指向的地址中的数据。
在声明时,*变量名
表示该变量作为一个指针;在其他时刻,*变量名
表示该指针指向的地址中的数据。
取地址&
获取变量的内存地址,并将其赋值给相应的指针
4. 指针的算数操作
指针与整形的算数操作不同于一般的数字加减,而是与指针类型绑定的,指针的算数操作是在当前指针所指向的地址往前或往后移动k个指针类型长度的地址,计算后的指针不一定会指向具有有效数据的地址,所以在进行指针算数操作的时候需小心。
- 数组名可以看作是指向数组第一个元素的指针,指针的各种操作都适用于数组名,但是数组名不能被重新赋值,因为数组是静态的,数组名代表了当前作用域唯一的一个数组,不可能像指针那样指向其他地址。
指针之间的相减操作返回的是指针地址之间的距离,并且是分正负的,这个距离也与指针类型相绑定。
string str[] = {
"1","2","3" };
//取地址
string *s1 = &str[0];
string *s2 = &str[1];
//解引用
cout << s1 << ' ' << *s1 << endl;
cout << s2 << ' ' << *s2 << endl;
//算数操作
cout << s1 + 1 << ' ' << *(s1 + 1) << endl;
cout << s2 - s1 << ' ' << s1 - s2 << endl; //1 -1
指针之间不能相加,因为没有意义。
4. const指针
指向const对象的指针const T *ptr1
ptr1指向的地址中的数据不能更改,但是它本身指向的地址可以更改;
const指针T *const ptr2
ptr2指向的地址不能修改,可以修改地址中保存的数据
指向const对象的const指针const T *const ptr3
ptr3指向的地址不能修改,该地址中的数据也不能更改
int numA = 3;
//不能修改数据,能改指针的地址
const int* ptr1 = &numA;
//*ptr1 = 8;
ptr1 = &numA + 1;
//能修改数据,不能改指针的地址
int* const ptr2 = &numA;
*ptr2 = 9;
//ptr2 = &numA + 1;
//不能修改数据,也不能改指针的地址
const int* const ptr3 = &numA;
//*ptr3 = 9;
//ptr3= &numA + 1;
5. 指针数组和数组指针
-
指针数组:
T *ptrArr[size]
,ptrArr是一个数组,数组中的每一个元素都是指针,指针类型是**T*
,可以使用*(ptrArr[i])
**来读取数组的元素; -
数组指针:
T (*arrPtr)[size]
,arrPtr指向某一个数组,需要把数组的地址赋值给arrPtr,解引用后就相当于数组,可以使用**(*arrPtr)[i]
**来读取数组的元素。
int num[5] = {
1,2,3,4,5 };
//指针数组:元素是指针的数组
int* ptrArr[5] = {
&num[0],&num[1],&num[2],&num[3],&num[4] };
for (int i = 0; i < 5; ++i) {
cout << ptrArr[i] << ':' << *(ptrArr[i]) << ' ';
}; cout << endl;
//数组指针:指向数组的指针
int(*arrPtr)[5] = #
for (int i = 0; i < 5; ++i) {
cout << ptrArr[i] << ':' << (*ptrArr)[i] << ' ';
}; cout << endl;
6. 指针的指针
指针可以指向任何变量或对象,当然也可以指向指针。
指针的指针在声明时,使用 ** 来表示指针指向的指针类型。指针的指针一般用于函数传参数时修改传入的指针。
const_cast和reinterpret_cast
- const_cast:修改类型的const或volatile属性,返回一个指向非const对象的指针或引用,此时可以通过该指针或引用改变其数据。
int numA = 3; const int* ptr1 = &numA; int* ptr2 = const_cast<int*>(ptr1); const int numB = 3; int& numb = const_cast<int&>(numB);
- reinterpret_cast:对变量的数据类型进行改变
7. 指针的一些应用
使用指针对两个数组进行排序
void sortPtr(int rNum, int* a, int aNum, int* b, int bNum) {
int* p, * q;
int* r = (int*)malloc((aNum + bNum) * sizeof(int));
int* res = r;
for (p = a, q = b; p < a + aNum && q < b + bNum;) {
if (*p > *q) {
*r++ = *q++;
}
else {
*r++ = *p++;
}
}
if (p < a + aNum) {
for (; p < a + aNum;)
*r++ = *p++;
}
else if (q < b + bNum) {
for (; q < b + bNum;)
*r++ = *q++;
}
for (r = res; r < res + aNum + bNum; ++r) {
cout << *r << ' ';
}
cout << endl;
}
使用指针遍历数组
//遍历数组时,要知道该数组的长度,以免指针超出数组的范围,导致读取数据失败
void testStr(string* p, int size) {
for (int i = 0; i < size; ++i) {
cout << p << endl;
++p;
}
}
//遍历字符数组时,
void testChar(const char* p) {
while (*p) {
cout << *p << endl;
++p;
}
}