C++语言程序设计——09 指针
一、取地址运算符
& 称为取地址运算符或引用,可以取地址,在其后跟一个变量名,表示取该变量在内存中的起始地址,地址一般以十六进制表示,通过地址结合指针,我们可以间接访问或修改变量的值。
例如,&a 表示获取变量 a 在内存中的地址。
例如,下面代码定义了两个 int 类型变量 a 和 b ,分别输出变量在内存中的地址:
#include <iostream>
using namespace std;
int main()
{
int a = 10;
int b = 100;
cout << a << endl << &a << endl;
cout << b << endl << &b << endl;
return 0;
}
运行结果如下,由于a 和 b 都是 int 类型(通常占 4 个字节)。开头的 0x 是十六进制的明确标识,地址相差 4(十六进制中 4 - 0 = 4),正好对应一个 int 类型的字节大小,说明这两个变量在内存中是连续存储的:
二、指针变量
(一)指针变量的声明
指针是一个特殊的变量,存储的不是普通的数据(如整数、字符等),而是目标变量的内存地址。声明的格式如下:
数据类型 *指针变量名;
这里的数据类型表示该指针所指向目标变量的数据类型。在声明变量时,* 表示 “这是一个指针变量”,例如 * a 代表它是一个指针变量。
int* a; // 声明一个指向int类型的指针变量a
char* p; // 声明一个指向char类型的指针变量p
float *x, *y; // 声明两个指向float类型的指针变量x和y
要注意,* 的优先级低于逗号,所以声明多个指针变量时,不能这样声明: float * x, y;
(二)指针变量的初始化
声明指针变量后,需要对其进行初始化,否则将是一个野指针(指向不确定的内存地址)。
我们可以将其指向一个已经定义的变量,由于不知道该定义变量的地址,可以首先通过 & 来获取变量地址,然后将其赋值给指针,如下:
int x = 10; // 定义一个int变量x,值为10
int* a = &x; // 定义指针a,指向x的地址
通过 & 取地址,来获取变量 x 的内存起始地址。【之后 a 存储的是 x 的地址】
这里两行代码以下面这张图理解,假如 0x1234 是其地址:

(三)解引用运算符
通过指针间接访问其指向的变量的值,称为解引用,解引用运算符也是 * 符号实现,格式如下:
*指针变量名
例如,访问 a 指向的值并输出,然后再通过指针修改 a 的值,
cout << *a;
*a = 20;
完整代码如下:
#include <iostream>
using namespace std;
int main()
{
int x = 10; // 定义一个int变量x,值为10
int* a = &x; // 定义指针a,指向x的地址
cout << *a << endl; // 解引用a,访问a指向的变量x的值,输出:10
*a = 20; // 通过解引用修改指向的变量的值,此时x的值变为20
cout << x << endl; // 输出:20
}

再举一个例子,如下代码:
int* p,a = 5;
p = &a;
int* q,b = 10;
q = &b;
相当于此时,*p 的值为5,*q 的值为10。
例如我们进行以下操作:
c = *p + 1;
d = *q - 2;
另外,如果有多层指针,一层指针只修改变量,二层指针可以修改另外一个指针的指向:
int a = 10;
int *p = &a; // 存a的地址
int **pp = &p; // 存p的地址
cout << a << " " << *p << " " << **pp; // 输出 10 10 10
三、指针与数组
1、获取数组中某个特定元素的地址
例如,我们可以通过取地址符 & 获取数组中某个特定元素的地址,并将其赋值给一个指针。
&数组名[数字] // 该元素在内存中的地址
例如,下面代码定义了一维数组:
#include <iostream>
using namespace std;
int main()
{
int arr[6] = {0, 10, 20, 30, 40, 50};
int* p = &arr[3]; // 通过 &arr[0] 获取数组第一个元素的地址,并赋值给指针 p
cout << "arr[3] 的值: " << arr[3] << endl;
cout << "arr[3] 的地址: " << &arr[3] << endl;
cout << "指针 p 存储的地址: " << p << endl;
return 0;
}

2、获取指针指定的变量的值
例如,下面代码定义了一维数组,通过使用 *p 访问 p 指向的变量的值:
#include <iostream>
using namespace std;
int main()
{
int arr[6] = {0, 10, 20, 30, 40, 50};
int* p = &arr[3]; // 通过 &arr[0] 获取数组第一个元素的地址,并赋值给指针 p
cout << "arr[3] 的值: " << arr[3] << endl;
cout << "arr[3] 的地址: " << &arr[3] << endl;
cout << "指针 p 存储的地址: " << p << endl;
cout << "通过 p 解引用访问的值: " << *p << endl;
return 0;
}
也就是指针 p 存储的是地址,*p是对指针的解引用,得到该地址上存储的值:
3、指针算术移动指针,指向数组中的其他元素
当我们将一个整数 i 加到一个指针上时,例如 :
p + i //指针向后移动 i 个元素的位置
此时,指针并不是简单地增加 i 个字节,而是增加 i * sizeof(指针指向的数据类型) 个字节,从而指针可以方便地在一个连续的数据块(如数组)中移动。
&数组名[某数字+i] //数组中某个元素向后移动 i 个位置后的元素的地址
例如,下面代码,执行p++。也就是 p=p+1,指针向后移动 1 个元素的位置,也就是向后移动一个 int 类型的大小(注:已经定义数据类型为 int):
#include <iostream>
using namespace std;
int main()
{
int arr[6] = {0, 10, 20, 30, 40, 50};
int* p = &arr[0];
cout << "p 指向的地址(arr[0]):" << p << endl;
cout << "p 指向的值:" << *p << endl;
p++; // 指针向后移动一个 int 的大小(通常是 4 个字节)
cout << "p++ 后指向的地址 (arr[1]): " << p << endl;
cout << "p++ 后指向的值: " << *p << endl;
return 0;
}
地址进行了改变,p = p + 1,指针向后移动一个 int 的大小(通常是 4 个字节),同时进行 *p,也可以发现改变:
四、指针与字符串
首先,要注意 C 风格字符串中,以一个隐藏的空字符 ‘\0’ 作为结束标志:
char str[] = "hello";
如下图:
通过& 字符数组名 [数字],可以让一个字符指针指向字符串(字符数组)中的特定字符:
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char str[] = "hello";
char* p = &str[1];
cout << "指针 p 指向的地址: " << (void*)p << endl; // (void*)p是为了让 cout 输出地址而不是把它当作字符串来打印,输出 0x101
cout << "指针 p 指向的字符: " << *p << endl; // 输出 'e'
return 0;
}

和数组一样,通过解引用字符,获取它所指向的具体字符:
cout << "*(p + 0) 的值: " << *(p + 0) << endl;
移动指针,字符指针加上一个整数 i,会指向当前位置往后的第 i 个字符的地址:
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char str[] = "hello";
char* p = &str[1];
cout << "指针 p 指向的地址: " << (void*)p << endl; // (void*)p是为了让 cout 输出地址而不是把它当作字符串来打印,输出 0x101
cout << "指针 p 指向的字符: " << *p << endl; // 输出 'e'
char* q = p + 2; // q 指向 p 后面第 2 个字符的地址
cout << "q 指向的地址: " << (void*)q << endl;
cout << "q 指向的字符: " << *q << endl; // 输出 'l'
return 0;
}

指针应用场景
两个变量交换
可以通过指针来交换两个变量的值:
void swap_num(int* a, int* b)
{
int temp = *a; // 解引用 a,获取它指向的值
*a = *b; // 解引用 b,将其值赋给 a 指向的变量
*b = temp; // 将临时变量的值赋给 b 指向的变量
}
如下代码:
#include <iostream>
using namespace std;
void swap_num(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int x = 10, y = 20;
cout << "交换前: x = " << x << ", y = " << y << endl;
// 传递变量的地址给函数
swap_num(&x, &y);
cout << "交换后: x = " << x << ", y = " << y << endl;
return 0;
}

在 C++ 中,对于交换场景,更推荐使用“&”,因为它语法更简洁,且同样能达到传递地址的效果,同时更安全。
#include <iostream>
using namespace std;
void swap_num(int& a, int& b) // 使用引用作为参数
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int x = 10, y = 20;
cout << "交换前: x = " << x << ", y = " << y << endl;
swap_num(x, y); // 直接传递变量名
cout << "交换后: x = " << x << ", y = " << y << endl;
return 0;
}
遍历数组
例如,下面代码,是定义了一个一维数组,然后通过指针遍历数组,输出地址和值:
#include <iostream>
using namespace std;
int main()
{
int arr[6] = {0, 10, 20, 30, 40, 50};
int num = sizeof(arr) / sizeof(arr[0]); // 计算数组元素个数
// 使用指针遍历数组
int* p = arr; // p 指向数组开头,和 int* p = &arr[0] 一样;
for (int i = 0; i < num; i++) {
cout << "arr[" << i << "] = " << p[i] << " (地址: " << p + i << ")" << endl; // p[i] 或者 *(p + i) 都可以
}
// 移动指针
p = arr; // 重置 p 到数组开头,和 p=&arr[0] 一样
for (int i = 0; i < num; i++) {
cout << "当前指针指向的值: " << *p << endl;
p++; // 指针向后移动一位
}
return 0;
}
更多推荐


所有评论(0)