操作与用法 |
C |
C++ |
指针的基本操作
& :为获取地址操作符(取地址);
* :即表示指针(变量),如int* p; ;又为解引用操作符,通过指针访问或修改其所指向的值,如*p 。
|
#include <stdio.h>
int main() {
int a = 10; // 定义一个整数变量 a
int* p = &a; // 定义一个指针变量 p,并指向 a 的地址
printf("Value of a: %d\n", *p); // 通过指针访问 a 的值
return 0;
}
#include <stdio.h>
int main() {
int x = 10;
int* p; // 声明一个指向 int 类型的指针
p = &x; // p 存储 x 的值
*p = 20; // 修改 x 值为 20
printf("Value of x: %d\n", *p);
return 0;
}
#include <stdio.h>
int main() {
int var = 42;
int* ptr = &var;
printf("Address of var: %p\n", (void*)&var); // 输出变量的地址
printf("Address stored in ptr: %p\n", (void*)ptr); // 打印存储在指针中的地址
printf("Value at the address stored in ptr: %d\n", *ptr); ///打印存储在指针地址处的值
return 0;
}
🡮
Address of var: 0000001BC06FF644
Address stored in ptr: 0000001BC06FF644
Value at the address stored in ptr: 42
|
#include <iostream>
#include <format>
int main() {
int a = 10; // 定义一个整数变量
int* p = &a; // 定义一个指针变量,并指向 a 的地址
std::cout << std::format("Value of a: {}\n", * p); // 通过指针访问 a 的值
return 0;
}
#include <iostream>
#include <format>
int main() {
int x = 10;
int* p; // 声明一个指向 int 类型的指针
p = &x; // p 存储 x 的值
*p = 20; // 修改 x 值为 20
std::cout << std::format("Value of x: {}\n", * p);
return 0;
}
#include <iostream>
#include <format>
int main() {
int var = 42;
int* ptr = &var;
std::cout << std::format("Address of var: {}\n", (void*)&var); // 输出变量的地址
std::cout << std::format("Address stored in ptr: {}\n", (void*)ptr); // 输出变量的地址
std::cout << std::format("Value at the address stored in ptr: {}\n", *ptr); ///打印存储在指针地址处的值
return 0;
}
🡮
Address of var: 0x56a7b5f944
Address stored in ptr: 0x56a7b5f944
Value at the address stored in ptr: 42
|
指针与数组
数组名实际上是一个常量指针,指向数组的第一个元素。
一个多维数组可以看作是数组的数组,例如int a[3][4] 表示一个包含3个元素的一维数组,而每个元素又是一个包含4个整数的数组。在多维数组中,a[i][j] 的地址可以用&a[i][i] 表示;a 是一个指向a[0] 的指针,a[0] 是一个指向数组的指针(即指向int[4] 的指针)。a 的类型是int(*)[4] ,表示一个指向包含4个整数数组的指针;a+i 表示第i 行的地址,其类型仍然是int(*)[4] ;*(a+i) 等价于a[i] ,是一个int[4] 数组;*(*(a+i)+j) 等价于a[i][j] 。
可以通过将多维数组看作一块连续的内存,使用一维指针访问。
|
#include <stdio.h>
int main() {
int arr[5] = { 1,4,9,16,25 };
int* p = arr; // 等价于 int *p = &arr[0];
printf("%d\n", *p);
p += 1;
printf("%d\n", *p);
printf("%d\n", *(p + 1));
return 0;
}
#include <stdio.h>
int main() {
int a[3][4] = {
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
};
// 用指针访问
int (*p)[4] = a; // 指向 int[4]的指针
printf("a[1][2] = %d\n", *(*(p + 1) + 2));
// 遍历多维数组
for (int i = 0;i < 3;i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", *(*(p + i) + j));
}
printf("\n");
}
return 0;
}
🡮
a[1][2] = 7
1 2 3 4
5 6 7 8
9 10 11 12
🤖 代码解释
声明指针并指向二维数组。定义一个指针p ,指向int[4] (一维数组,长度为4);即将p 指向二维数组a 的起始地址。二维地址的首地址实际上是第一个一维数组的地址。
printf("a[1][2] = %d\n", *(*(p + 1) + 2));
声明指针并指向二维数组。表达式分解:p + 1 中p 指向二维数组的第0行,加1后指向第1行({5,6,7,8} );*(p+1) 为解引用,的到第1行(一个一维数组的地址,等同于a[1] );*(p + 1)+2 则将第1行的地址向后偏移2个元素,指向a[1][2] (值为7);*(*(p + 1) + 2) 为解引用,取值,结果为7。
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", *(*(p + i) + j));
}
printf("\n");
}
遍历二维数组。外层循环for (int i = 0; i < 3; i++) 为遍历二维数组的每一行;内层循环for (int j = 0; j < 4; j++) 为遍历当前行的每一列;*(*(p + i) + j) 中,p + i 为指针p 指向第i 行(一个一维数组的地址);*(p + i) 解引用,得到第i 行数组;*(*(p + i) + j) 是将第i 行的起始地址偏移j 个元素;*(*(p + i) + j) 解引用,取第i 行第j 列的值。
#include <stdio.h>
int main() {
int a[3][4] = {
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
};
// 将二维数组当作一维数组处理
int *p = &a[0][0];
// 按一维方式访问元素
for (int i = 0; i < 12; i++) {
printf("%d ", *(p + i));
}
printf("\n");
return 0;
}
🡮
1 2 3 4 5 6 7 8 9 10 11 12
|
#include <iostream>
int main() {
int arr[5] = { 1, 4, 9, 16, 25 };
int* p = arr; // 数组名即为指向第一个元素的指针
std::cout << *p << std::endl;
p += 1;
std::cout << *p << std::endl;
std::cout << *(p + 1) << std::endl;
return 0;
}
#include <iostream>
int main() {
int a[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 使用指针访问
int (*p)[4] = a; // 指向包含 4 个整数数组的指针
std::cout << "a[1][2] = " << *(*(p + 1) + 2) << std::endl;
// 遍历多维数组
for (int i = 0;i < 3;++i) {
for (int j = 0;j < 4;++j) {
std::cout << *(*(p + i) + j) << " ";
}
std::cout << std::endl;
}
return 0;
}
🡮
a[1][2] = 7
1 2 3 4
5 6 7 8
9 10 11 12
🤖 代码解释
声明指针并指向二维数组。定义一个指针p ,指向一个包含 4 个整数的数组(int[4] );即p 指向二维数组a 的起始地址(二维数组的首地址等同于第0行的起始地址)。
std::cout << "a[1][2] = " << *(*(p + 1) + 2) << std::endl;
使用指针访问二维数组的元素。表达式分解:p + 1 中指针p 从第0行移动到第1行,指向{5,6,7,8} ;*(p + 1) 为解引用,得到第1行的起始地址(等价于a[1] );*(p + 1) + 2 为在第1行的基础上偏移2个元素,指向a[1][2] (值为7);*(*(P + 1) + 2) 再次解引用,取出指针指向的值,即7 。
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
std::cout << *(*(p + i) + j) << " ";
}
std::cout << std::endl;
}
遍历二维数组。外层循环for (int i = 0; i < 3; ++i) 遍历二维数组的每一行(i 表示行号);内层循环for (int j = 0; j < 4; ++j) 遍历当前行的每一列(j 表示列号)。通过指针访问二维数组,*(p + i) 指向第i 行(等价于a[i] );*(p + i) + j 在第i 行上偏移j 个元素,指向a[i][j] ;*(*(p + i) + j) 解引用,取出值。
#include <iostream>
int main() {
int a[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 将二维数组的首地址赋值给一维指针
int* p = &a[0][0];
// 按一维方式访问二维数组
for (int i = 0;i < 12;++i) {
std::cout << *(p + i) << " ";
}
std::cout << std::endl;
return 0;
}
🡮
1 2 3 4 5 6 7 8 9 10 11 12
|
指针与函数
使用指针可以实现函数参数的传址调用。
🤖 代码解释
void swap(int *x, int *y) {
函数的定义。定义一个函数swap ,用于交换两个整数变量的值。参数int *x 和int *y 分别指向一个整数的指针,为其地址。void 表示该函数没有返回值。
临时变量保存值。定义一个临时变量temp ,将x 所指向的值存储到temp 中;*x 是解引用操作,获取指针x 指向内存地址中存储的值。
将y 指针指向地址(变量)的值赋值给x 指针指向地址(变量)的值。*y 为解引用,获取其指向地址的值;*x 通过解引用操作,访问x 指针所指向的变量,并修改x 指向内存地址(变量)所存储的值。
将临时变量的值赋值给x 指针指向地址(变量)的值,完成两个整数变量值的交换。
C/C++ 中,指针可以作为函数的返回值,允许函数返回一个内存地址,以便对该地址进行操作。
|
#include <stdio.h>
void swap(int* x, int* y) {
int temp = *x;
*x = *y;
*y = temp;
}
int main() {
int a = 10, b = 20;
swap(&a, &b); // 交换 a 和 b 的值
printf("a = %d; b = %d", a, b);
return 0;
}
在 C 中, 最常见的做法是返回指向局部静态变量或动态分配内存的指针。需要特别小心避免返回指向局部变量的指针,因为局部变量的生命周期在函数返回后结束。
返回指向静态变量的指针。示例中num 是静态变量,在函数调用外仍然存在,因此返回的指针是有效的。
#include <stdio.h>
int* getPointerToStaticVariable() {
static int num = 10; // 静态变量,生命周期直到程序结束
return #
}
int main() {
int* ptr = getPointerToStaticVariable();
printf("Value: %d\n", *ptr);
return 0;
}
返回指向动态分配内存的指针。
#include <stdio.h>
#include <stdlib.h>
int* createArray(int size) {
int* arr = (int*)malloc(size * sizeof(int));
if (arr == NULL) {
printf("Memory allocation failed!\n");
return NULL;
}
for (int i = 0; i < size; i++) {
arr[i] = i;
}
return arr;
}
int main() {
int* arr = createArray(5);
if (arr != NULL) {
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr); // 释放动态内存
}
return 0;
}
🤖 代码解释
int* arr = (int*) malloc(size * sizeof(int));
int* arr 中arr 是一个指向整数的指针(int* ),为用于存储一个整数数组的起始地址。动态内存分配后,arr 会指向malloc 返回的内存块起始地址。
(int*) 是强制类型转换。malloc 会返回一个void* 类型的指针(通用指针),可以指向任何类型的内存块。在 C 中,void* 可以被隐式转换为其他类型的指针,因此(int*) 强制类型转换是可选的。
malloc(size * sizeof(int)) 为动态分配指定字节数的内存块,返回该内存块的起始地址。其中参数size * sizeof(int) 表示要分配的内存大小(以字节为单位)。size 是一个变量,表示需要的数组元素个数。sizeof(int) 是一个运算符,用来计算int 类型的大小(以字节为单位)。
- 如果分配成功,
malloc 返回分配的内存块的起始地址;如果分配失败(例如内存不足),malloc 返回NULL 。
|
#include <iostream>
#include <format>
void swap(int* x, int* y) {
int temp = *x;
*x = *y;
*y = temp;
}
int main() {
int a = 10, b = 20;
swap(&a, &b); // 交换 a 和 b 的值
std::cout << std::format("a = {}; b = {}\n", a, b);
return 0;
}
C++ 允许与 C 相同的指针操作,同时提供更多的功能,如智能指针(std::unique_ptr 和std::shared_ptr ),可以帮助避免内存泄漏。
返回普通指针。在 C++ 中,new 用于动态内存分配,delete[] 用于释放内存。
#include <iostream>
#include <format>
int* createArray(int size) {
int* arr = new int[size]; // 动态分配内存
for (int i = 0;i < size;i++) {
arr[i] = i;
}
return arr;
}
int main() {
int* arr = createArray(5);
for (int i = 0;i < 5;i++) {
std::cout << std::format("{} ", arr[i]);
}
std::cout << std::endl;
delete[] arr; // 释放动态分配的内存
return 0;
}
返回智能指针。std::unique_ptr 或std::shared_ptr 会自动管理内存的释放,因此可以避免内存泄漏。
#include <iostream>
#include <format>
std::unique_ptr<int[]> createArray(int size) {
std::unique_ptr<int[]> arr = std::make_unique<int[]>(size);
for (int i = 0;i < size;i++) {
arr[i] = i;
}
return arr;
}
int main() {
std::unique_ptr<int[]> arr = createArray(5);
for (int i = 0;i < 5;i++) {
std::cout << std::format("{} ", arr[i]);
}
std::cout << std::endl;
// 不需要手动调用 delete, 智能指针会自动释放内存
return 0;
}
🤖 代码解释
std::unique_ptr<int[]> createArray(int size)
std::unique_ptr<int[]> 为定义函数createArray 的返回类型,表示一个管理动态分配的整数数组的智能指针。std::unique_ptr 是 C++11 引入的智能指针,确保动态内存在使用完成后自动释放,防止内存泄漏。模板参数int[] 指定这是一个动态分配的整数数组。
- 参数
int size ,指定数组的大小,即需要分配的整数个数。
std::unique_ptr<int[]> arr = std::make_unique<int[]>(size);
std::make_unique<int[]>(size) 是一个用于创建智能指针的函数模板,其动态分配一个大小为size 的整数数组,并返回一个std::unique_ptr<int[]> ,指向这个数组的起始地址。使用std::make_unique 比直接调用new 更安全,能够避免内存泄漏。
std::unique_ptr<int[]> arr 中,arr 是一个std::unique_ptr 类型的变量,负责管理分配的整数数组。当arr 离开作用域时,指针会自动释放所分配的内存,无需显示调用delete[] 。
|
指针与结构体
|
定义结构体后,可以通过指针访问和操作其所指向的成员。
#include <stdio.h>
// 定义结构体
struct Point {
int x;
int y;
};
int main() {
struct Point p = { 10,20 }; // 定义和初始化结构体变量
struct Point* ptr = &p; // 定义指向结构体的指针
// 通过指针访问结构体成员
printf("x: %d, y: %d\n", ptr->x, ptr->y); // 使用`->`操作符,等价于(*ptr).x
ptr->x = 30; // 修改结构体成员
printf("Modified x: %d\n", ptr->x);
return 0;
}
🡮
x: 10, y: 20
Modified x: 30
使用malloc 动态分配内存。通过free 释放动态分配的内存,防止内存泄漏。
#include <stdio.h>
#include <stdlib.h> // 包含 malloc 和 free 函数
struct Point {
int x;
int y;
};
int main() {
// 动态分配内存
struct Point* p = (struct Point*)malloc(sizeof(struct Point));
if (p == NULL) {
printf("Memory allocation failed\n");
return 1;
}
// 使用动态分配的结构体
p->x = 10;
p->y = 20;
printf("x: %d, y: %d\n", p->x, p->y);
free(p); // 释放内存
return 0;
}
|
在 C++ 中,用new 和delete 替代 C 中的malloc 和free 。
#include <iostream>
#include <format>
struct Point {
int x;
int y;
};
int main() {
Point* p = new Point; // 动态分配结构体
p->x = 10;
p->y = 20;
std::cout << std::format("x: {}, y: {}", p->x, p->y);
delete p; // 释放内存
return 0;
}
用智能指针(如std::uniuque_ptr 和std::shared_ptr )自动管理内存,避免手动释放。
#include <iostream>
#include <format>
struct Point {
int x;
int y;
};
int main() {
// 使用 unique_ptr 管理结构体
std::unique_ptr<Point> p = std::make_unique<Point>();
p->x = 10;
p->y = 20;
std::cout << std::format("x: {}, y: {}\n", p->x, p->y);
// 无需手动 delete, uniuqe_ptr 自动释放内存
return 0;
}
|
函数指针
函数指针是一种指针类型变量,其存储的是函数的地址。通过函数指针可以间接调用函数。其用途包括回调函数,允许将函数作为参数传递,实现动态行为;动态函数选择,在运行时选择调用不同的函数;策略模式,指针可以指向多种实现,动态切换逻辑;事件处理,在图形用户界面(graphical user interface, GUI) 等编程中,用于事件绑定和触发;动态加载,使用指针绑定动态链接库中的函数。声明函数指针的基本语法为ReturnType (*PointerVariableName)(ParemeterTypeList); ,初始化函数指针的语法为PointerVariableName = FunctionName; 。
|
#include <stdio.h>
// 定义一个函数
int add(int a, int b) {
return a + b;
}
int main() {
// 声明一个函数指针,指向返回类型 int, 接收两个 int 类型的函数参数
int (*func_ptr)(int, int);
// 将函数地址赋值给指针(函数名即为函数地址)
func_ptr = add;
// 通过函数指针调用函数
printf("Result: %d\n", func_ptr(5, 3));
return 0;
}
#include <stdio.h>
void execute_callback(void (*callback)(void)) {
// 调用回调函数
callback();
}
void say_hello() {
printf("Hello, World!\n");
}
int main() {
execute_callback(say_hello); // 传递函数指针
return 0;
}
|
#include <iostream>
#include <format>
int multiply(int a, int b) {
return a * b;
}
int main() {
// 声明并初始化函数指针
int (*func_ptr)(int, int) = multiply;
// 通过函数指针调用函数
std::cout << std::format("Result: {}\n", func_ptr(3, 5));
return 0;
}
类成员函数的调用需要通过对象调用。
#include <iostream>
#include <format>
class Calculator {
public:
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
};
int main() {
Calculator calc;
// 声明一个指向成员函数的指针
int (Calculator:: * func_ptr)(int, int);
// 指向 add 函数
func_ptr = &Calculator::add;
std::cout << std::format("Add: {}\n", (calc.*func_ptr)(3, 4));
// 指向 multiply 函数
func_ptr = &Calculator::multiply;
std::cout << std::format("Multiply: {}\n", (calc.*func_ptr)(3, 4));
return 0;
}
|
指针数组
指针数组是一个数组,其中每一个元素都是一个指针,指向其他变量或对象的地址。常用应用包括字符串管理,用于管理字符串集合;动态数据结构,用于管理动态分配的内存块;多态性,存储不同派生类对象的指针;多维数组,通过灵活的指针操作模拟多维数组;回调机制,管理回调函数的指针。指针数组的基本语法为Type* arrayName[size] ,其中Type* 表示数组中的每个元素都是指向Type 的指针;size 为数组中指针的数量。可以用arrayName[index] 获取特定索引的指针;通过*arrayName[index] 解引用访问指针指向的值。
|
#include <stdio.h>
int main() {
int a = 10, b = 20, c = 30;
int* ptrArray[3]; // 声明一个包含 3 个 int 指针的数组
// 将变量的地址存储到指针数组中
ptrArray[0] = &a;
ptrArray[1] = &b;
ptrArray[2] = &c;
// 通过指针数组访问变量的值
for (int i = 0;i < 3;i++) {
printf("Value at ptrArray[%d]: %d\n", i, *ptrArray[i]);
}
return 0;
}
🡮
Value at ptrArray[0]: 10
Value at ptrArray[1]: 20
Value at ptrArray[2]: 30
在 C 中,字符串是字符数组,指针数组可以用来存储多个字符串。
#include <stdio.h>
int main() {
// 声明一个指针数组用于存储字符串
const char* strArray[] = { "Hello","World","C programming" };
// 访问并打印字符串
for (int i = 0;i < 3;i++) {
printf("String %d: %s\n", i + 1, strArray[i]);
}
return 0;
}
🡮
String 1: Hello
String 2: World
String 3: C programming
|
示例代码使用一个指针数组管理动态分配的内存;将动态分配的整数存储到数组中;打印动态分配的整数;最后释放内存,避免内存泄漏。
#include <iostream>
#include <format>
int main() {
int* ptrArray[3]; // 声明一个包含 3 个 int 指针的数组
// 动态分配内存并存储到数组中
for (int i = 0;i < 3;i++) {
ptrArray[i] = new int(i + 1); // 分配内存并赋值
}
// 通过指针数组访问值
for (int i = 0;i < 3;i++) {
std::cout << std::format("Value at ptrArray[{}]: {}\n", i, *ptrArray[i]);
}
// 释放动态分配的内存
for (int i = 0;i < 3;i++) {
delete ptrArray[i];
}
return 0;
}
🡮
Value at ptrArray[0]: 1
Value at ptrArray[1]: 2
Value at ptrArray[2]: 3
在 C++ 中,指针数组可以存储对象的指针。
#include <iostream>
#include <format>
class Animal {
public:
virtual void speak() const {
std::cout << "Animal speaks" << std::endl;
}
};
class Dog :public Animal {
public:
void speak()const override {
std::cout << "Dog barks" << std::endl;
}
};
class Cat : public Animal {
public:
void speak()const override {
std::cout << "Cat meows" << std::endl;
}
};
int main() {
Animal* animals[2]; // 声明一个指针数组用于存储 Animal 对象的指针
// 存储派生类对象的指针
animals[0] = new Dog();
animals[1] = new Cat();
// 通过数组访问对象
for (int i = 0;i < 2;i++) {
animals[i]->speak();
}
// 释放内存
for (int i = 0;i < 2;i++) {
delete animals[i];
}
return 0;
}
|
多级指针
在 C/C++ 中,多级指针(即指向指针的指针)是指一个指针指向另一个指针,这样可以在内存中间接引用数据。多级指针常用于处理二维数组、动态分配的多维数组及需要复杂内存管理的场景。
一级指针是指向某个数据类型的指针,例如, int* ptr 是一个指向int 类型变量的指针。
int a = 10;
int* ptr = &a;
二级指针是指向一级指针的指针,即一个指针保存另一个指针的地址。常见于处理指针数组或动态分配内存时。
int a = 10;
int* ptr1 = &a; // 一级指针
int** ptr2 = &ptr1; // 二级指针
printf("%d\n", **ptr2); // 输出 10
上述代码中,ptr2 是一个指向ptr1 的指针,而ptr1 是指向a 的指针。**ptr2 表示解引用ptr2 两次,最终获得a 的值。
三级指针是指向二级指针的指针,可以用于更复杂的数据结构,如动态二维数组。
int a = 10;
int* ptr1 = &a;
int** ptr2 = &ptr1;
int*** ptr3 = &ptr2;
printf("%d\n", ***ptr3); // 输出 10
|
示例为使用二级指针int** arr 来处理动态分配的二维数组。首先分配一个指向行的指针数组,然后为每一个行分配一个int 类型的数组,最后使用数组并释放内存。
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows = 3, cols = 4;
int** arr = (int**)malloc(rows * sizeof(int*));
for (int i = 0;i < rows;i++) {
arr[i] = (int*)malloc(cols * sizeof(int));
}
for (int i = 0;i < rows;i++) {
for (int j = 0;j < cols;j++) {
arr[i][j] = i * cols + j;
}
}
for (int i = 0;i < rows;i++) {
for (int j = 0;j < cols;j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
for (int i = 0; i < rows;i++) {
free(arr[i]);
}
free(arr);
return 0;
}
🡮
0 1 2 3
4 5 6 7
8 9 10 11
🤖 代码解释
int** arr = (int**)malloc(rows * sizeof(int*));
- 使用
malloc 动态分配一个指针数组arr , 其包含rows 个int* (指向整型数组的指针)。sizeof(int*) 表示每个指针的大小,rows * sizeof(int*) 是分配的内存总大小。这里分配了一个包含 3 个指针的数组,每个指针将指向一行。
for (int i = 0; i < rows; i++) {
arr[i] = (int*)malloc(cols * sizeof(int));
}
- 遍历每一行(共 3 行)。
arr[i] = (int*)malloc(cols * sizeof(int)); 为每一行动态分配存储cols 个整数的内存空间。每个arr[i] 是指向整型数组的指针,大小为cols * sizeof(int) ,即每行有4个整数。
|
指针数组可以用于模拟多维数组,通过数组中的每个指针指向不同的数组。
#include <iostream>
int main() {
int rows = 2, cols = 3;
// 创建一个指针数组用于存储行
int** matrix = new int* [rows];
// 为每一行分配内存
for (int i = 0;i < rows;i++) {
matrix[i] = new int[cols];
for (int j = 0;j < cols;j++) {
matrix[i][j] = i * cols + j + 1; // 赋值
}
}
// 打印二维数组
for (int i = 0;i < rows;i++) {
for (int j = 0;j < cols;j++) {
std::cout << matrix[i][j] << " ";
}
std::cout << std::endl;
}
// 释放内存
for (int i = 0;i < rows;i++) {
delete[] matrix[i];
}
delete[] matrix;
return 0;
}
🤖 代码解释
int** matrix = new int* [rows];
- 动态分配一个指针数组
matrix ,包含rows 个int* (指向整型数组的指针),可以理解为分配了存储行指针的空间。
for (int i = 0; i < rows; i++) {
matrix[i] = new int[cols]; // 为每一行分配存储 cols 个整数的内存
for (int j = 0; j < cols; j++) {
matrix[i][j] = i * cols + j + 1; // 初始化每个元素
}
}
- 外层循环遍历每一行。
matrix[i] = new int[cols]; 为每一个行分配存储cols 个整数的连续内存。
- 内层循环遍历当前行的每一列。
matrix[i][j] = i * cols + j + 1; 为按顺序给矩阵的每个元素赋值。
|
空指针和悬空指针
|
在 C/C++ 中,空指针(Null Pointer)和悬空指针(Dangling Pointer)是两种不同的指针状态,对其处理直接关系到程序的安全性和稳定性。
空指针是指指针没有指向任何有效的内存位置,通常用于初始化指针变量,表示该指针不指向任何对象。空指针的值通常为NULL ,或者在 C++11 中可以使用nullptr (推荐写法)。
C 风格空指针 |
C++11 引入的空指针 |
int* ptr = NULL; |
int* ptr = nullptr; |
在 C 中,NULL 是一个宏,通常被定义为((void*)0) ,表示指针不指向任何有效的地址 |
nullptr 为类型安全的空指针 |
检查空指针,可以使用[C]if (ptr == NULL){} 和[C++]if (ptr == nullptr){} 。
空指针的主要用途是在程序中检测和初始化指针,以确保其不会指向不确定的内存区域,如用于指针初始化;用于表示函数返回的“无效”值;及在某些函数中检查指针是否有效等。
悬空指针是指指向一个已被释放或不再有效的内存地址的指针。通常情况下,悬空指针是因为对象已经被销毁或内存已经被释放,但指针仍然指向旧地址。悬空指针的访问会导致未定义行为,可能会引起程序崩溃或错误的数据访问。悬空指针产生的原因有释放内存后仍然访问指向该内存的指针;指向局部变量的指针在函数返回后仍然有效(局部变量已销毁);指向已删除对象的指针。
int* ptr = (int*)malloc(sizeof(int)); // 动态分配内存
*ptr = 42;
free(ptr); // 释放内存
// 此时 ptr 是一个悬空指针,访问它是未定义的行为
*ptr = 10; // 访问悬空指针,导致错误
上述示例,ptr 指向的内存被free 释放后,ptr 变成了悬空指针。如果继续访问,就会发生未定义的行为。为了避免悬空指针,可以将指针设置为NULL 或nullptr ,
free(ptr);
ptr = nullptr; // 使 ptr 不在悬空
在对象的析构函数中处理指针的释放和设为空。
class MyClass {
private:
int* ptr;
public:
MyClass() { ptr = new int; }
~MyClass() { delete ptr; ptr = nullptr; }
};
使用智能指针来自动管理内存,从而避免悬空指针问题。
std::unique_ptr<int> ptr = std::make_unique<int>(42);
空指针和悬空指针区别如表,
特性 |
空指针(Null Pointer) |
悬空指针(Dangling Pointer) |
定义 |
不指向任何有效内存地址,通常为NULL 或nullptr |
指向已释放或无效的内存地址 |
产生原因 |
初始化指针,指示指针未分配内存 |
在释放内存后指针没有被置为NULL ,导致指向无效内存 |
使用后果 |
不访问空指针,避免崩溃 |
访问悬空指针会导致程序崩溃或未定义行为 |
解决办法 |
检查指针是否为nullptr 或NULL |
释放内存后将指针设置为nullptr |
|