文章目录
- C++数组地址传递与数据影响:深入理解指针与内存
-
- [📝 前言](#📝 前言)
- 一、数组的本质:连续内存块
-
- [1.1 内存中的数组](#1.1 内存中的数组)
- [1.2 数组名的秘密](#1.2 数组名的秘密)
- [二、值传递 vs 地址传递](#二、值传递 vs 地址传递)
-
- [2.1 普通变量的值传递](#2.1 普通变量的值传递)
- [2.2 数组的地址传递](#2.2 数组的地址传递)
- 三、多种数组参数写法
-
- [3.1 三种等价写法](#3.1 三种等价写法)
- [3.2 sizeof的陷阱](#3.2 sizeof的陷阱)
- 四、二维数组的地址传递
-
- [4.1 二维数组的内存布局](#4.1 二维数组的内存布局)
- [4.2 使用指针的指针](#4.2 使用指针的指针)
- 五、保护数组数据:const的使用
-
- [5.1 只读访问](#5.1 只读访问)
- 六、实际应用场景
-
- [6.1 数组排序函数](#6.1 数组排序函数)
- [6.2 矩阵运算](#6.2 矩阵运算)
- 七、常见错误与陷阱
-
- [7.1 返回局部数组的地址](#7.1 返回局部数组的地址)
- [7.2 数组越界](#7.2 数组越界)
- 八、性能考量与最佳实践
-
- [8.1 传递引用避免拷贝](#8.1 传递引用避免拷贝)
- [8.2 最佳实践总结](#8.2 最佳实践总结)
- 九、总结
- 十、练习题
- 结语
- 练习题答案详解
C++数组地址传递与数据影响:深入理解指针与内存
📝 前言
在C++编程中,理解数组的地址传递机制是掌握指针和内存管理的核心。很多初学者在这里容易混淆:为什么函数内修改数组会影响原数组?数组名到底代表什么?今天,让我们通过详细的示例和解释,彻底搞懂这个问题。
一、数组的本质:连续内存块
1.1 内存中的数组
当我们声明一个数组时,实际上是在内存中申请了一段连续的空间:
cpp
int numbers[5] = {10, 20, 30, 40, 50};
内存布局示意图:
内存地址:0x1000 0x1004 0x1008 0x100C 0x1010
┌────────┬────────┬────────┬────────┬────────┐
│ 10 │ 20 │ 30 │ 40 │ 50 │
└────────┴────────┴────────┴────────┴────────┘
↑
numbers[0]
(数组名指向这里)
1.2 数组名的秘密
cpp
#include <iostream>
using namespace std;
int main() {
int arr[5] = {10, 20, 30, 40, 50};
cout << "数组名 arr 的值: " << arr << endl;
cout << "第一个元素的地址 &arr[0]: " << &arr[0] << endl;
cout << "第二个元素的地址 &arr[1]: " << &arr[1] << endl;
// 验证数组名就是指针
cout << "使用数组名访问第一个元素: " << *arr << endl;
cout << "使用指针运算访问第二个元素: " << *(arr + 1) << endl;
return 0;
}
输出结果:
数组名 arr 的值: 0x7ffd5e3b4a00
第一个元素的地址 &arr[0]: 0x7ffd5e3b4a00
第二个元素的地址 &arr[1]: 0x7ffd5e3b4a04
使用数组名访问第一个元素: 10
使用指针运算访问第二个元素: 20
关键发现: 数组名就是指向第一个元素的指针!
二、值传递 vs 地址传递
2.1 普通变量的值传递
cpp
#include <iostream>
using namespace std;
void modifyValue(int x) {
x = 100; // 修改的是副本
cout << "函数内: x = " << x << endl;
}
int main() {
int num = 10;
cout << "修改前: num = " << num << endl;
modifyValue(num);
cout << "修改后: num = " << num << endl;
// 输出仍然是10,因为传递的是副本
return 0;
}
2.2 数组的地址传递
cpp
#include <iostream>
using namespace std;
void modifyArray(int arr[], int size) {
cout << "函数内修改数组..." << endl;
for(int i = 0; i < size; i++) {
arr[i] *= 2; // 直接修改原数组
}
// 验证函数内数组地址
cout << "函数内数组地址: " << arr << endl;
}
int main() {
int numbers[] = {1, 2, 3, 4, 5};
int size = sizeof(numbers) / sizeof(numbers[0]);
cout << "原始数组: ";
printArray(numbers, size);
cout << "main中数组地址: " << numbers << endl;
modifyArray(numbers, size);
cout << "修改后数组: ";
printArray(numbers, size); // 数组被修改了!
return 0;
}
void printArray(int arr[], int size) {
for(int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
输出结果:
原始数组: 1 2 3 4 5
main中数组地址: 0x7ffd5e3b4a00
函数内数组地址: 0x7ffd5e3b4a00
修改后数组: 2 4 6 8 10
核心差异:
- 普通变量:传递的是值的副本,函数内修改不影响原变量
- 数组:传递的是地址,函数内通过地址直接操作原数组
三、多种数组参数写法
3.1 三种等价写法
cpp
#include <iostream>
using namespace std;
// 写法1:不指定大小的数组形式
void func1(int arr[]) {
arr[0] = 100;
}
// 写法2:指定大小的数组形式(编译器忽略大小)
void func2(int arr[10]) {
arr[1] = 200;
}
// 写法3:指针形式(最本质的写法)
void func3(int* arr) {
arr[2] = 300;
}
// 验证三种写法的地址
void verifyAddress(int arr[], int* ptr) {
cout << "数组参数地址: " << arr << endl;
cout << "指针参数地址: " << ptr << endl;
cout << "它们是否相等? " << (arr == ptr ? "是" : "否") << endl;
}
int main() {
int data[5] = {1, 2, 3, 4, 5};
cout << "原始数据: ";
for(int i = 0; i < 5; i++) cout << data[i] << " ";
cout << endl;
func1(data);
func2(data);
func3(data);
cout << "修改后数据: ";
for(int i = 0; i < 5; i++) cout << data[i] << " ";
cout << endl;
verifyAddress(data, data);
return 0;
}
3.2 sizeof的陷阱
cpp
#include <iostream>
using namespace std;
void printArrayInfo(int arr[]) {
// 这里arr已经退化为指针,sizeof(arr)得到的是指针大小
cout << "函数内 - sizeof(arr): " << sizeof(arr) << " 字节" << endl;
cout << "函数内 - 数组大小: " << sizeof(arr) / sizeof(int) << endl;
}
int main() {
int arr[10] = {0};
cout << "main中 - sizeof(arr): " << sizeof(arr) << " 字节" << endl;
cout << "main中 - 数组大小: " << sizeof(arr) / sizeof(int) << endl;
printArrayInfo(arr);
return 0;
}
输出结果:
main中 - sizeof(arr): 40 字节
main中 - 数组大小: 10
函数内 - sizeof(arr): 8 字节 (64位系统上指针大小)
函数内 - 数组大小: 2
重要提醒: 在函数内无法通过sizeof获取数组实际大小,必须额外传递size参数!
四、二维数组的地址传递
4.1 二维数组的内存布局
cpp
#include <iostream>
using namespace std;
void process2DArray(int matrix[][3], int rows) {
cout << "处理二维数组..." << endl;
for(int i = 0; i < rows; i++) {
for(int j = 0; j < 3; j++) {
matrix[i][j] = (i + 1) * (j + 1) * 10;
}
}
}
void print2DArray(int matrix[][3], int rows) {
for(int i = 0; i < rows; i++) {
for(int j = 0; j < 3; j++) {
cout << matrix[i][j] << "\t";
}
cout << endl;
}
}
int main() {
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
cout << "原始二维数组:" << endl;
print2DArray(matrix, 2);
cout << "\n内存地址分析:" << endl;
cout << "matrix: " << matrix << endl;
cout << "matrix[0]: " << matrix[0] << endl;
cout << "matrix[1]: " << matrix[1] << endl;
cout << "行间距: " << (matrix[1] - matrix[0]) << " 个int单位" << endl;
process2DArray(matrix, 2);
cout << "\n修改后的二维数组:" << endl;
print2DArray(matrix, 2);
return 0;
}
4.2 使用指针的指针
cpp
#include <iostream>
using namespace std;
// 动态分配二维数组
void dynamic2DArray(int*** arr, int rows, int cols) {
// 分配行指针数组
*arr = new int*[rows];
// 为每一行分配内存
for(int i = 0; i < rows; i++) {
(*arr)[i] = new int[cols];
}
// 初始化数据
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
(*arr)[i][j] = i * cols + j + 1;
}
}
}
// 修改动态二维数组
void modifyDynamicArray(int** arr, int rows, int cols) {
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
arr[i][j] *= 2;
}
}
}
int main() {
int** myArray = nullptr;
int rows = 3, cols = 4;
// 创建动态二维数组
dynamic2DArray(&myArray, rows, cols);
cout << "初始化的动态数组:" << endl;
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
cout << myArray[i][j] << "\t";
}
cout << endl;
}
// 修改数组
modifyDynamicArray(myArray, rows, cols);
cout << "\n修改后的动态数组:" << endl;
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
cout << myArray[i][j] << "\t";
}
cout << endl;
}
// 释放内存
for(int i = 0; i < rows; i++) {
delete[] myArray[i];
}
delete[] myArray;
return 0;
}
五、保护数组数据:const的使用
5.1 只读访问
cpp
#include <iostream>
using namespace std;
// const保护:只能读取,不能修改
void readOnlyArray(const int* arr, int size) {
// arr[0] = 100; // 编译错误!不能修改const数据
cout << "只读访问数组: ";
for(int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
// 混合使用:指针本身是const,指向的数据也可变
void fixedPointer(int* const ptr, int size) {
// ptr = nullptr; // 编译错误!不能修改指针本身
ptr[0] = 999; // 可以修改指向的数据
}
// 双重const:都不能修改
void totallyFixed(const int* const ptr, int size) {
// ptr[0] = 100; // 错误!不能修改数据
// ptr = nullptr; // 错误!不能修改指针
}
int main() {
int data[] = {1, 2, 3, 4, 5};
readOnlyArray(data, 5);
cout << "修改前: " << data[0] << endl;
fixedPointer(data, 5);
cout << "修改后: " << data[0] << endl;
return 0;
}
六、实际应用场景
6.1 数组排序函数
cpp
#include <iostream>
#include <algorithm>
using namespace std;
// 冒泡排序 - 直接操作原数组
void bubbleSort(int* arr, int size) {
for(int i = 0; i < size - 1; i++) {
for(int j = 0; j < size - i - 1; j++) {
if(arr[j] > arr[j + 1]) {
// 交换元素
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
// 查找最大值 - 返回指针
int* findMax(int* arr, int size) {
if(size <= 0) return nullptr;
int* maxPtr = arr;
for(int i = 1; i < size; i++) {
if(arr[i] > *maxPtr) {
maxPtr = &arr[i];
}
}
return maxPtr;
}
int main() {
int scores[] = {85, 92, 78, 96, 88, 76};
int size = sizeof(scores) / sizeof(scores[0]);
cout << "原始成绩: ";
for(int i = 0; i < size; i++) cout << scores[i] << " ";
cout << endl;
// 排序
bubbleSort(scores, size);
cout << "排序后成绩: ";
for(int i = 0; i < size; i++) cout << scores[i] << " ";
cout << endl;
// 查找最高分
int* highest = findMax(scores, size);
if(highest) {
cout << "最高分: " << *highest << endl;
cout << "位置: " << highest - scores << endl;
}
return 0;
}
6.2 矩阵运算
cpp
#include <iostream>
using namespace std;
// 矩阵加法
void matrixAdd(int result[][3], const int A[][3], const int B[][3], int rows) {
for(int i = 0; i < rows; i++) {
for(int j = 0; j < 3; j++) {
result[i][j] = A[i][j] + B[i][j];
}
}
}
// 矩阵乘法
void matrixMultiply(int result[][3], const int A[][3], const int B[][3], int n) {
// 初始化结果矩阵
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
result[i][j] = 0;
for(int k = 0; k < n; k++) {
result[i][j] += A[i][k] * B[k][j];
}
}
}
}
int main() {
int A[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int B[3][3] = {{9, 8, 7}, {6, 5, 4}, {3, 2, 1}};
int C[3][3] = {0};
cout << "矩阵A:" << endl;
printMatrix(A, 3);
cout << "矩阵B:" << endl;
printMatrix(B, 3);
matrixAdd(C, A, B, 3);
cout << "A + B:" << endl;
printMatrix(C, 3);
matrixMultiply(C, A, B, 3);
cout << "A × B:" << endl;
printMatrix(C, 3);
return 0;
}
void printMatrix(int matrix[][3], int n) {
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
cout << matrix[i][j] << "\t";
}
cout << endl;
}
cout << endl;
}
七、常见错误与陷阱
7.1 返回局部数组的地址
cpp
#include <iostream>
using namespace std;
// 错误示例:返回局部数组的地址
int* badFunction() {
int localArray[5] = {1, 2, 3, 4, 5};
return localArray; // 危险!函数结束后localArray被销毁
}
// 正确做法1:使用static
int* goodFunction1() {
static int staticArray[5] = {1, 2, 3, 4, 5};
return staticArray; // 静态变量在整个程序运行期间存在
}
// 正确做法2:动态分配
int* goodFunction2() {
int* dynamicArray = new int[5]{1, 2, 3, 4, 5};
return dynamicArray; // 动态内存需要手动释放
}
int main() {
// 错误用法
int* ptr1 = badFunction();
// cout << ptr1[0]; // 未定义行为!可能崩溃或得到错误值
// 正确用法1
int* ptr2 = goodFunction1();
cout << "静态数组: " << ptr2[0] << endl;
// 正确用法2
int* ptr3 = goodFunction2();
cout << "动态数组: " << ptr3[0] << endl;
delete[] ptr3; // 记得释放内存
return 0;
}
7.2 数组越界
cpp
#include <iostream>
using namespace std;
void dangerousOperation(int* arr, int size) {
// 危险!没有检查边界
for(int i = 0; i <= size; i++) { // 应该是 i < size
arr[i] = i; // 最后一次循环会越界
}
}
void safeOperation(int* arr, int size) {
if(arr == nullptr || size <= 0) return;
for(int i = 0; i < size; i++) {
arr[i] = i;
}
}
int main() {
int buffer[5] = {0};
cout << "执行安全操作..." << endl;
safeOperation(buffer, 5);
cout << "执行危险操作..." << endl;
dangerousOperation(buffer, 5); // 可能会破坏栈或堆
return 0;
}
八、性能考量与最佳实践
8.1 传递引用避免拷贝
cpp
#include <iostream>
#include <chrono>
using namespace std;
// 传递大型数组 - 高效(只传递地址)
void processLargeArray(int* arr, int size) {
// 直接操作原数组
for(int i = 0; i < size; i++) {
arr[i] = arr[i] * 2 + 1;
}
}
// 模拟大型数据处理
int main() {
const int SIZE = 10000000; // 1千万个元素
int* bigData = new int[SIZE];
// 初始化
for(int i = 0; i < SIZE; i++) {
bigData[i] = i;
}
auto start = chrono::high_resolution_clock::now();
// 处理数据 - 只需传递地址,非常快
processLargeArray(bigData, SIZE);
auto end = chrono::high_resolution_clock::now();
auto duration = chrono::duration_cast<chrono::milliseconds>(end - start);
cout << "处理 " << SIZE << " 个元素耗时: "
<< duration.count() << " 毫秒" << endl;
delete[] bigData;
return 0;
}
8.2 最佳实践总结
cpp
#include <iostream>
using namespace std;
// ✅ 推荐:传递数组和大小
void bestPractice1(int* arr, int size) {
if(arr == nullptr || size <= 0) return;
for(int i = 0; i < size; i++) {
// 安全的操作
}
}
// ✅ 推荐:使用const保护只读数据
void bestPractice2(const int* arr, int size) {
// 只读操作
}
// ✅ 推荐:返回动态数组时明确文档
/**
* @brief 创建并初始化一个数组
* @param size 数组大小
* @return 动态分配的数组指针,需要调用者delete[]
*/
int* bestPractice3(int size) {
if(size <= 0) return nullptr;
int* arr = new int[size];
for(int i = 0; i < size; i++) {
arr[i] = i;
}
return arr;
}
// ✅ 推荐:使用现代C++的array或vector
#include <vector>
void bestPractice4(std::vector<int>& vec) {
for(auto& v : vec) {
v *= 2;
}
}
九、总结
核心要点回顾
- 数组名就是指针:指向第一个元素的常量指针
- 地址传递:传递数组给函数时,传递的是地址,函数内修改会影响原数组
- sizeof陷阱:在函数内无法通过sizeof获取数组大小
- const保护:使用const可以防止函数修改数组内容
- 边界检查:始终确保数组访问不越界
- 内存管理:动态分配的数组需要手动释放
面试常见问题
Q: 数组作为参数传递时,是值传递还是引用传递?
A: 本质上是地址传递(类似引用传递),传递的是指向第一个元素的指针。
Q: 为什么不能在函数内用sizeof获取数组大小?
A: 因为数组作为参数时会退化为指针,sizeof得到的是指针的大小。
Q: 如何防止函数修改数组内容?
A: 使用const关键字:void func(const int* arr, int size)
Q: 二维数组作为参数时,为什么必须指定第二维的大小?
A: 因为编译器需要知道每行的长度才能正确计算地址偏移。
十、练习题
练习1:实现数组反转函数
cpp
// 要求:实现一个函数,原地反转数组
void reverseArray(int* arr, int size) {
// 你的代码
}
练习2:实现数组去重
cpp
// 要求:删除有序数组中的重复元素,返回新长度
int removeDuplicates(int* arr, int size) {
// 你的代码
}
练习3:实现矩阵转置
cpp
// 要求:实现方阵的原地转置
void transposeMatrix(int matrix[][3], int n) {
// 你的代码
}
结语
理解数组的地址传递机制是C++编程的重要基石。通过本文的详细示例和解释,相信你已经掌握了数组作为函数参数的本质。记住:数组名就是指针,传递的是地址,修改会直接影响原数组。在实际编程中,要注意边界检查、使用const保护数据,并正确处理动态内存。
希望这篇文章对你有帮助!如果有任何问题,欢迎在评论区讨论。Happy Coding! 🚀
练习题答案详解
练习1:数组反转函数
cpp
#include <iostream>
using namespace std;
// 解法1:使用双指针法(最经典)
void reverseArray(int* arr, int size) {
if (arr == nullptr || size <= 1) return; // 边界检查
int left = 0;
int right = size - 1;
while (left < right) {
// 交换左右两个元素
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
// 移动指针
left++;
right--;
}
}
// 解法2:使用for循环
void reverseArray2(int* arr, int size) {
if (arr == nullptr || size <= 1) return;
for (int i = 0; i < size / 2; i++) {
int temp = arr[i];
arr[i] = arr[size - 1 - i];
arr[size - 1 - i] = temp;
}
}
// 解法3:使用指针运算
void reverseArray3(int* arr, int size) {
if (arr == nullptr || size <= 1) return;
int* start = arr;
int* end = arr + size - 1;
while (start < end) {
int temp = *start;
*start = *end;
*end = temp;
start++;
end--;
}
}
// 测试函数
void testReverseArray() {
cout << "=== 测试数组反转 ===" << endl;
int arr1[] = {1, 2, 3, 4, 5};
int size1 = sizeof(arr1) / sizeof(arr1[0]);
cout << "原始数组: ";
for (int i = 0; i < size1; i++) cout << arr1[i] << " ";
cout << endl;
reverseArray(arr1, size1);
cout << "反转后: ";
for (int i = 0; i < size1; i++) cout << arr1[i] << " ";
cout << endl << endl;
// 测试偶数个元素
int arr2[] = {10, 20, 30, 40};
int size2 = sizeof(arr2) / sizeof(arr2[0]);
cout << "偶数个元素 - 原始: ";
for (int i = 0; i < size2; i++) cout << arr2[i] << " ";
cout << endl;
reverseArray2(arr2, size2);
cout << "反转后: ";
for (int i = 0; i < size2; i++) cout << arr2[i] << " ";
cout << endl;
}
int main() {
testReverseArray();
return 0;
}
练习2:数组去重(有序数组)
cpp
#include <iostream>
using namespace std;
// 解法1:双指针法(最优解)
int removeDuplicates(int* arr, int size) {
if (arr == nullptr || size == 0) return 0;
// slow指向不重复序列的最后一个位置
int slow = 0;
// fast遍历整个数组
for (int fast = 1; fast < size; fast++) {
// 发现新元素
if (arr[fast] != arr[slow]) {
slow++; // 移动slow到下一个位置
arr[slow] = arr[fast]; // 将新元素放到slow位置
}
}
// 返回新数组的长度(slow是索引,长度需要+1)
return slow + 1;
}
// 解法2:计数法(更容易理解)
int removeDuplicates2(int* arr, int size) {
if (arr == nullptr || size == 0) return 0;
int newSize = 1; // 至少有一个元素
for (int i = 1; i < size; i++) {
// 如果当前元素不等于前一个元素
if (arr[i] != arr[newSize - 1]) {
arr[newSize] = arr[i];
newSize++;
}
}
return newSize;
}
// 解法3:通用版本(适用于任何数组,不仅限于有序)
#include <algorithm>
int removeDuplicatesUnsorted(int* arr, int size) {
if (arr == nullptr || size <= 1) return size;
// 先排序
sort(arr, arr + size);
// 然后去重
return removeDuplicates(arr, size);
}
// 测试函数
void testRemoveDuplicates() {
cout << "=== 测试数组去重 ===" << endl;
// 测试用例1:普通情况
int arr1[] = {1, 1, 2, 2, 2, 3, 4, 4, 5};
int size1 = sizeof(arr1) / sizeof(arr1[0]);
cout << "原始数组: ";
for (int i = 0; i < size1; i++) cout << arr1[i] << " ";
cout << endl;
int newSize1 = removeDuplicates(arr1, size1);
cout << "去重后: ";
for (int i = 0; i < newSize1; i++) cout << arr1[i] << " ";
cout << " (新长度: " << newSize1 << ")" << endl << endl;
// 测试用例2:全部相同
int arr2[] = {7, 7, 7, 7, 7};
int size2 = sizeof(arr2) / sizeof(arr2[0]);
cout << "全部相同 - 原始: ";
for (int i = 0; i < size2; i++) cout << arr2[i] << " ";
cout << endl;
int newSize2 = removeDuplicates2(arr2, size2);
cout << "去重后: ";
for (int i = 0; i < newSize2; i++) cout << arr2[i] << " ";
cout << " (新长度: " << newSize2 << ")" << endl << endl;
// 测试用例3:无重复
int arr3[] = {1, 2, 3, 4, 5};
int size3 = sizeof(arr3) / sizeof(arr3[0]);
cout << "无重复 - 原始: ";
for (int i = 0; i < size3; i++) cout << arr3[i] << " ";
cout << endl;
int newSize3 = removeDuplicates(arr3, size3);
cout << "去重后: ";
for (int i = 0; i < newSize3; i++) cout << arr3[i] << " ";
cout << " (新长度: " << newSize3 << ")" << endl;
}
int main() {
testRemoveDuplicates();
return 0;
}
练习3:矩阵转置(方阵)
cpp
#include <iostream>
using namespace std;
// 解法1:原地转置(只适用于方阵)
void transposeMatrix(int matrix[][3], int n) {
if (matrix == nullptr || n <= 1) return;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
// 交换 matrix[i][j] 和 matrix[j][i]
int temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
}
}
// 解法2:使用指针运算
void transposeMatrix2(int (*matrix)[3], int n) {
if (matrix == nullptr || n <= 1) return;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
// 使用指针访问
int temp = *(*(matrix + i) + j);
*(*(matrix + i) + j) = *(*(matrix + j) + i);
*(*(matrix + j) + i) = temp;
}
}
}
// 解法3:非方阵的转置(需要新矩阵)
void transposeNonSquare(int** src, int** dest, int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
dest[j][i] = src[i][j];
}
}
}
// 打印矩阵
void printMatrix(int matrix[][3], int n) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cout << matrix[i][j] << "\t";
}
cout << endl;
}
}
// 打印任意大小矩阵
void printAnyMatrix(int** matrix, int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << matrix[i][j] << "\t";
}
cout << endl;
}
}
// 测试函数
void testTransposeMatrix() {
cout << "=== 测试矩阵转置 ===" << endl;
// 3x3矩阵
int matrix[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
cout << "原始矩阵:" << endl;
printMatrix(matrix, 3);
transposeMatrix(matrix, 3);
cout << "\n转置后:" << endl;
printMatrix(matrix, 3);
// 测试4x4矩阵
cout << "\n测试4x4矩阵:" << endl;
int matrix2[4][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 16}
};
// 由于函数固定为3列,这里需要另一种方式
// 使用动态方法
int** mat = new int*[4];
for (int i = 0; i < 4; i++) {
mat[i] = new int[4];
for (int j = 0; j < 4; j++) {
mat[i][j] = i * 4 + j + 1;
}
}
cout << "原始4x4矩阵:" << endl;
printAnyMatrix(mat, 4, 4);
// 手动转置
for (int i = 0; i < 4; i++) {
for (int j = i + 1; j < 4; j++) {
int temp = mat[i][j];
mat[i][j] = mat[j][i];
mat[j][i] = temp;
}
}
cout << "\n转置后:" << endl;
printAnyMatrix(mat, 4, 4);
// 释放内存
for (int i = 0; i < 4; i++) {
delete[] mat[i];
}
delete[] mat;
// 测试非方阵
cout << "\n测试3x2矩阵转置:" << endl;
int** src = new int*[3];
int** dest = new int*[2]; // 转置后变成2x3
for (int i = 0; i < 3; i++) {
src[i] = new int[2];
for (int j = 0; j < 2; j++) {
src[i][j] = i * 2 + j + 1;
}
}
for (int i = 0; i < 2; i++) {
dest[i] = new int[3];
}
cout << "原始3x2矩阵:" << endl;
printAnyMatrix(src, 3, 2);
transposeNonSquare(src, dest, 3, 2);
cout << "\n转置后的2x3矩阵:" << endl;
printAnyMatrix(dest, 2, 3);
// 释放内存
for (int i = 0; i < 3; i++) delete[] src[i];
delete[] src;
for (int i = 0; i < 2; i++) delete[] dest[i];
delete[] dest;
}
int main() {
testTransposeMatrix();
return 0;
}
综合测试程序
cpp
#include <iostream>
using namespace std;
// 将所有练习整合在一起测试
int main() {
cout << "=================================" << endl;
cout << " C++数组练习题答案 - 完整测试 " << endl;
cout << "=================================" << endl << endl;
// 练习1:数组反转
cout << "【练习1】数组反转" << endl;
cout << "-------------------" << endl;
int test1[] = {1, 2, 3, 4, 5, 6, 7};
int size1 = sizeof(test1) / sizeof(test1[0]);
cout << "原始数组: ";
for (int x : test1) cout << x << " ";
cout << endl;
reverseArray(test1, size1);
cout << "反转数组: ";
for (int x : test1) cout << x << " ";
cout << endl << endl;
// 练习2:数组去重
cout << "【练习2】数组去重" << endl;
cout << "-------------------" << endl;
int test2[] = {1, 1, 2, 2, 2, 3, 3, 4, 5, 5, 5};
int size2 = sizeof(test2) / sizeof(test2[0]);
cout << "原始数组: ";
for (int i = 0; i < size2; i++) cout << test2[i] << " ";
cout << endl;
int newSize = removeDuplicates(test2, size2);
cout << "去重数组: ";
for (int i = 0; i < newSize; i++) cout << test2[i] << " ";
cout << " (长度: " << newSize << ")" << endl << endl;
// 练习3:矩阵转置
cout << "【练习3】矩阵转置" << endl;
cout << "-------------------" << endl;
int test3[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
cout << "原始矩阵:" << endl;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cout << test3[i][j] << " ";
}
cout << endl;
}
transposeMatrix(test3, 3);
cout << "\n转置矩阵:" << endl;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cout << test3[i][j] << " ";
}
cout << endl;
}
cout << "\n=================================" << endl;
cout << " 测试完成 " << endl;
cout << "=================================" << endl;
return 0;
}
关键点总结
-
数组反转:
- 双指针法效率最高 O(n)
- 注意边界条件:空数组、单个元素
- 循环条件
left < right
-
数组去重:
- 双指针法:slow指向不重复序列末尾,fast遍历
- 适用于有序数组
- 时间复杂度 O(n),空间复杂度 O(1)
-
矩阵转置:
- 方阵可以原地转置
- 只需遍历上三角或下三角
- 非方阵需要新矩阵存储
这些解法都是面试中常见的优化解法,掌握它们对理解指针和数组操作很有帮助!