指针
5.1 地址与指针的概念
在 C++ 中,地址与指针是重要的概念,它们在内存管理和程序设计中起到了关键的作用。在本节中,我们将学习地址和指针的概念以及如何在 C++ 中使用它们。
5.1.1 内存地址
内存是计算机用来存储数据和程序的地方。每个内存单元都有唯一的地址。地址是用来访问内存中特定位置的标识符。在 C++ 中,可以使用取地址运算符 &
来获取变量的内存地址。
代码示例:
#include <iostream>
int main()
{
int num = 10;
std::cout << "变量num的内存地址: " << &num << std::endl;
return 0;
}
输出:
变量num的内存地址:0x7ffc2d6f003c
在上面的例子中,我们声明了一个整型变量 num
,使用取地址运算符 &
获取了变量 num
的内存地址,并通过 std::cout
输出了该地址。
5.1.2 变量的地址
在 C++ 中,我们可以将变量的地址存储在指针变量中。指针变量是一种特殊的变量,它用于存储其他变量的地址。可以使用指针变量来间接访问原始变量。
代码示例:
#include <iostream>
int main()
{
int num = 10;
int *ptr = # // 定义一个整型指针变量ptr,将num的地址赋值给ptr
std::cout << "变量num的内存地址: " << ptr << std::endl;
std::cout << "指针变量ptr的值: " << *ptr << std::endl;
return 0;
}
输出:
变量num的内存地址:0x7ffc2d6f003c
指针变量ptr的值:10
在上面的例子中,我们定义了一个整型指针变量 ptr
,将变量 num
的地址赋值给了 ptr
。然后,我们通过 *ptr
访问了原始变量的值。
5.1.3 变量的指针
在 C++ 中,可以使用指针来操作变量本身。通过改变指针的值,我们可以改变指针所指向的变量的值。
代码示例:
#include <iostream>
int main()
{
int num = 10;
int *ptr = # // 定义一个整型指针变量ptr,将num的地址赋值给ptr
*ptr = 20; // 改变指针所指向的变量
std::cout << "变量num的值: " << num << std::endl;
return 0;
}
输出:
变量num的值:20
在上面的例子中,我们通过指针变量 ptr
改变了 num
的值。通过 *ptr = 20
,我们将指针所指向的变量的值改为了 20。
总结:
- 地址是用来访问内存中特定位置的标识符。
- 可以使用取地址运算符 `&获取变量的内存地址。
- 指针变量是用来存储其他变量的地址的特殊变量。
- 可以使用指针变量来间接访问原始变量。
- 可以通过改变指针的值来改变指针所指向的变量的值。
5.2 指针变量及指针运算
5.2.1 指针变量
指针是一种特殊类型的变量,其存储的是内存地址。通过指针可以访问和修改对应地址上存储的数据。下面是一个通过指针变量存取变量值的例子:
【例 5-1】通过指针变量存取变量的值
#include <iostream>
using namespace std;
int main()
{
int x = 10; // 定义一个整数变量 x,并赋值为 10
int *ptr; // 定义一个指向整数类型的指针变量 ptr
ptr = &x; // 将指针变量 ptr 指向 x 的地址
cout << "x 的值为:" << x << endl;
cout << "通过指针变量访问 x 的值为:" << *ptr << endl;
*ptr = 20; // 修改 ptr 所指向的地址中存储的值
cout << "修改后 x 的值为:" << x << endl;
return 0;
}
输出结果:
x 的值为:10
通过指针变量访问 x 的值为:10
修改后 x 的值为:20
在上述例子中,我们通过指针变量 ptr
来存取变量x
的值。首先,我们将指针变量 ptr
指向变量 x
的地址,然后通过指针变量 ptr
访问变量 x
的值,并且可以通过修改指针变量 ptr
所指向地址中的值来修改变量 x
的值。
【例 5-2】输入 a 和 b 两个整数,按从小到大的顺序输出
#include <iostream>
using namespace std;
void sort(int *a, int *b)
{
if (*a > *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
}
int main()
{
int a, b;
cout << "请输入两个整数:" << endl;
cin >> a >> b;
sort(&a, &b);
cout << "从小到大排序后的结果为:" << a << ", " << b << endl;
return 0;
}
输出结果:
请输入两个整数:
5
3
从小到大排序后的结果为:3, 5
在上述例子中,我们定义了一个函数 sort
,该函数接受两个指向整数的指针作为参数。在函数内部,我们通过判断指针所指向的变量的大小关系,交换变量的值,从而实现从小到大的排序。
5.2.2 指针运算
指针变量与整数可以进行加减运算,称为指针运算。下面是一个指针与整数的加减运算的例子:
【例 5-3】指针与整数的加减运算
#include <iostream>
using namespace std;
int main()
{
int arr[] = {1, 2, 3, 4, 5};
int *ptr = arr; // 指向数组的第一个元素的指针
cout << "ptr 的初始值为:" << ptr << endl;
ptr += 2; // 将指针加 2
cout << "ptr 加 2 后的值为:" << ptr << endl;
ptr -= 1; // 将指针减 1
cout << "ptr 减 1 后的值为:" << ptr << endl;
return 0;
}
输出结果:
ptr 的初始值为:0x7ffee418b4a0
ptr 加 2 后的值为:0x7ffee418b4a8
ptr 减 1 后的值为:0x7ffee418b4a4
在上述例子中,我们定义了一个整数数组 arr
,并定义了一个指向数组第一个元素的指针 ptr
。通过指针加减整数的运算,可以使指针指向数组中的其他元素。
【例 5-4】指针的关系运算
#include <iostream>
using namespace std;
int main()
{
int arr[] = {1, 2, 3, 4, 5};
int *ptr = arr; // 指向数组的第一个元素的指针
cout << "ptr 的初始值为:" << ptr << endl;
cout << "指针与整数的关系运算:" << endl;
for(int i = 0; i < 5; i++)
{
cout << "arr + " << i << " 的值为:" << arr + i << endl;
if(arr + i == ptr)
{
cout << "arr + " << i << " 等于 ptr" << endl;
}
}
return 0;
}
输出结果:
ptr 的初始值为:0x7ffee418b4a0
指针与整数的关系运算:
arr + 0 的值为:0x7ffee418b4a0
arr + 0 等于 ptr
arr + 1 的值为:0x7ffee418b4a4
arr + 2 的值为:0x7ffee418b4a8
arr + 3 的值为:0x7ffee418b4ac
arr + 4 的值为:0x7ffee418b4b0
在上述例子中,我们通过指针与整数的关系运算来比较指针与数组元素的地址。可以看到,当两者相等时输出相应的信息。
C++学习笔记:指针与数组
5.3 指针与数组
5.3.1 使用指针处理数组
在C++中,指针与数组有着密切的关联。指针可以用来处理数组的元素。下面是一个例子,展示如何使用指针输出数组中的所有元素。
【例5-5】使用指针输出数组中的所有元素
#include <iostream>
using namespace std;
int main()
{
int arr[] = {1, 2, 3, 4, 5};
int *ptr = arr;
cout << "数组中的元素:";
for(int i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
{
cout << *(ptr + i) << " ";
}
return 0;
}
输出
数组中的元素:1 2 3 4 5
在上面的示例中,我们先定义了一个整型数组 arr
,然后创建了一个指向数组首元素的指针 ptr
,将其初始化为 arr
。然后我们使用指针进行遍历,通过 *(ptr + i)
来访问数组中的每个元素,并输出到控制台。
5.3.2 指针数组
指针数组是数组元素为指针类型的数组。我们可以使用指针数组来处理二维数组中的元素。下面是一个例子,展示如何用指针数组处理二维数组的元素。
【例5-6】用指针数组处理二维数组的元素
#include <iostream>
using namespace std;
int main()
{
int arr[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
int *ptr[3];
for(int i = 0; i < 3; i++)
{
ptr[i] = arr[i];
}
cout << "二维数组中的元素:";
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 4; j++)
{
cout << *(ptr[i] + j) << " ";
}
}
return 0;
}
输出
二维数组中的元素:1 2 3 4 5 6 7 8 9 10 11 12
在上面的示例中,我们定义了一个二维整型数组 arr
,然后创建了一个指针数组 ptr
。然后,我们使用一个循环将每行的首元素地址赋值给指针数组中的元素。最后,我们使用指针数组来遍历二维数组的元素,并输出到控制台。
5.3.3 多级指针
在C++中,我们还可以使用多级指针,即指针的指针,来进行更灵活的操作。下面是两个例子,展示了多级指针的应用。
【例5-7】二级指针的应用
#include <iostream>
using namespace std;
int main()
{
int num = 10;
int *ptr = #
int **pptr = &ptr;
cout << "变量num的值:" << num << endl;
cout << "一级指针ptr的值:" << *ptr << endl;
cout << "二级指针pptr的值:" << **pptr << endl;
return 0;
}
输出
变量num的值:10
一级指针ptr的值:10
二级指针pptr的值:10
在上面的示例中,我们首先定义了一个整型变量 num
,然后创建了一个指向 num
的指针 ptr
,最后创建了一个指向 ptr
的指针 pptr
。通过多级指针,我们可以访问到 num
的值。
【例5-8】二维数组的地址
#include <iostream>
using namespace std;
int main()
{
int arr[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
int (*ptr)[4] = arr;
cout << "二维数组的地址:" << ptr << endl;
return 0;
}
输出
二维数组的地址:008FF978
在上面的示例中,我们定义了一个二维整型数组 arr
,然后创建了一个指针 ptr
,将其初始化为 arr
。通过输出指针的值,我们可以获取到二维数组的地址。
5.3.4 数组指
数组指针是指向数组的指针类型。通过使用数组指针,我们可以方便地访问二维数组中的元素。下面是一个例子,展示了如何使用数组指针访问二维数组。
【例5-9】使用数组指针访问二维数组
#include <iostream>
using namespace std;
int main()
{
int arr[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
int (*ptr)[4];
ptr = arr;
cout << "二维数组的第二个元素:" << *(*ptr + 1) << endl;
return 0;
}
输出
二维数组的第二个元素:2
在上面的示例中,我们定义了一个二维整型数组 arr
,然后创建了一个二维数组指针 ptr
。然后,我们将指针 ptr
指向二维数组 arr
,通过 *(*ptr + 1)
来访问二维数组的第二个元素。
5.4 常量指针与指针常量
5.4.1 常量指针
常量指针是指指针指向的对象的值不能被修改,但指针本身可以改变指向其他对象。
【例5-10】常量指针的使用:
#include <iostream>
using namespace std;
int main()
{
int num = 10;
const int *ptr = # // 使用常量指针声明并指向num
cout << "num的值为:" << *ptr << endl; // 输出num的值
// *ptr = 20; // 错误,不能通过指针修改num的值
int anotherNum = 20;
ptr = &anotherNum; // 指针指向另一个对象
cout << "anotherNum的值为:" << *ptr << endl; // 输出anotherNum的值
return 0;
}
输出
num的值为:10
anotherNum的值为:20
在上述代码中,我们通过使用常量指针const int *ptr
,声明了一个指向num
的常量指针,并输出了num
的值。然后尝试修改num
的值,但由于ptr
是常量指针,所以无法修改。接着,我们声明了另一个变量anotherNum
并将ptr
指向它,并输出了anotherNum
的值。
5.4.2 指针常量
指针常量是指指针本身的值不能被修改,但指针指向的对象可以被修改。
【例5-11】指针常量的使用:
#include <iostream>
using namespace std;
int main()
{
int num = 10;
int *const ptr = # // 使用指针常量声明,且指向num
cout << "num的值为:" << *ptr << endl; // 输出num的值
*ptr = 20; // 允许通过指针修改num的值
cout << "num的新值为:" << *ptr << endl; // 输出新的num的值
// ptr = nullptr; // 错误,指针常量的值不能被修改
return 0;
}
输出
num的值为:10
num的新值为:20
在上述代码中,我们通过使用指针常量int *const ptr
,声明了一个指向num
的指针常量,并输出了num
的值。然后我们修改了num
的值,由于ptr
是指针常量,所以可以通过指针修改num
的值并输出新的值。但尝试修改ptr
的值则会报错。
5.5 动态内存分配
在C++中,动态内存分配是一种在程序运行时分配和释放内存的方法。与静态内存分配不同,动态内存分配可以根据程序的需要动态地请求和释放内存。这在一些情况下非常有用,例如在数组大小未知、对象数量不确定或需要在堆上分配内存的情况下。
5.5.1 分配单个存储空间
要在C++中分配单个存储空间,我们可以使用new
关键字。下面是一个分配单个整数存储空间的例子:
int* p = new int;
在上面的例子中,我们使用new int
来请求分配存储空间,并将返回的地址赋给指针变量p
。然后,我们可以使用指针变量p
来访问分配的内存空间。
在使用完动态分配的内存后,我们需要使用delete
关键字来释放这块内存,以免造成内存泄漏。释放内存的方法如下:
delete p;
在上面的例子中,我们使用delete p
来释放指针变量p
指向的内存空间。
5.5.2 分配多个连续的存储空间
除了分配单个存储空间外,我们还可以使用new
关键字来分配多个连续的存储空间,例如数组。下面是一个分配包含5个整数的数组的例子:
int* arr = new int[5];
在上面的例子中,我们使用new int[5]
来请求分配包含5个整数的数组,并将返回的数组的首地址赋给指针变量arr
。然后,我们可以使用指针变量arr
来访问分配的数组。
同样地,当我们使用完动态分配的数组后,我们需要使用delete[]
关键字来释放这块内存,以免造成内存泄漏。释放数组内存的方法如下:
delete[] arr;
在上面的例子中,我们使用delete[] arr
来释放指针变量arr
指向的数组的所有内存空间。
【例5-12】动态内存分配的使用
以下是一个关于动态内存分配的实际代码案例:
#include <iostream>
using namespace std;
int main()
{
// 分配单个存储空间
int* p = new int;
*p = 42;
cout << "Value of p: " << *p << endl;
delete p;
// 分配多个连续的存储空间
int size;
cout << "Enter the size of the array: ";
cin >> size;
int* arr = new int[size];
cout << "Enter " << size << " integers:" << endl;
for (int i = 0; i < size; i++)
{
cin >> arr[i];
}
cout << "You entered:" << endl;
for (int i = 0; i < size; i++)
{
cout << arr[i] << " ";
}
cout << endl;
delete[] arr;
return 0;
}
上面的代码演示了如何使用动态内存分配来分别分配单个存储空间和多个连续的存储空间,并通过输出来展示了动态内存分配的使用方法。
C++学习笔记
5.6 综合实例
【例 5-13】使用二级指针指向指针数组
问题描述:我们需要使用一个二级指针指向一个动态分配的指针数组,并对该数组进行操作。
解决方案:
#include <iostream>
using namespace std;
int main()
{
int length;
cout << "请输入数组长度:";
cin >> length;
int **ptrArr = new int*[length]; // 动态分配指针数组
// 给指针数组中的每个指针分配内存空间
for (int i = 0; i < length; i++)
{
ptrArr[i] = new int;
*ptrArr[i] = i;
}
// 输出指针数组中的值
for (int i = 0; i < length; i++)
{
cout << *ptrArr[i] << " ";
}
cout << endl;
// 释放内存空间
for (int i = 0; i < length; i++)
{
delete ptrArr[i];
}
delete[] ptrArr;
return 0;
}
输出
请输入数组长度:10
0 1 2 3 4 5 6 7 8 9
【例5-14】使用选择排序对字符串排序
问题描述:给定一个字符串数组,我们需要使用选择排序来对字符串进行排序。
解决方案:
#include <iostream>
#include <string>
using namespace std;
void selectionSort(string arr[], int length)
{
for (int i = 0; i < length - 1; i++)
{
int minIndex = i;
for (int j = i + 1; j < length; j++)
{
if (arr[j] < arr[minIndex])
{
minIndex = j;
}
}
swap(arr[i], arr[minIndex]);
}
}
int main()
{
int length;
cout << "请输入数组长度:";
cin >> length;
string *arr = new string[length];
for (int i = 0; i < length; i++)
{
cout << "请输入第" << i + 1 << "个字符串:";
cin >> arr[i];
}
selectionSort(arr, length);
cout << "排序后的字符串数组为:";
for (int i = 0; i < length; i++)
{
cout << arr[i] << " ";
}
cout << endl;
delete[] arr;
return 0;
}
输出
请输入数组的长度:10
请输入第1个字符串:w
请输入第2个字符串:e
请输入第3个字符串:r
请输入第4个字符串:t
请输入第5个字符串:y
请输入第6个字符串:u
请输入第7个字符串:i
请输入第8个字符串:o
请输入第9个字符串:p
请输入第10个字符串:a
排序后的字符串数组为:a e i o p r t u w y
【例5-15】报数出圈问题
问题描述:有n个人围成一圈,从第一个人开始报数,报到m的人出圈,然后从出圈的下一个人开始重新报数,直到剩下最后一个人。我们需要找到最后剩下的那个人。
解决方案:
#include <iostream>
using namespace std;
int josephusProblem(int n, int m)
{
int lastPerson = 0;
for (int i = 2; i <= n; i++)
{
lastPerson = (lastPerson + m) % i;
}
return lastPerson + 1;
}
int main()
{
int n, m;
cout << "请输入总人数:";
cin >> n;
cout << "请输入报数的数字:";
cin >> m;
int lastPerson = josephusProblem(n, m);
cout << "最后剩下的人是第" << lastPerson << "个人" << endl;
return 0;
}
输出
请输入总人数:5
请输入报数的数字:4
最后剩下的人是第1个人