在C++中,指针是非常重要的概念,它允许直接操作内存地址。以下是常见的指针类型及其操作:
1. 普通指针
普通指针是最基本的指针类型,用于存储某个变量的内存地址。通过指针,可以间接地访问和修改该变量的值。
int num = 10;
int* ptr = # // ptr是指向num的指针
*ptr = 20; // 修改num的值为20
常见操作:
*ptr
:解引用指针,访问指针指向的变量。ptr = &var
:获取变量的地址,并将其赋给指针。
2. 空指针(nullptr)
空指针是指向“无效”内存地址的指针。在C++中,可以使用nullptr
来表示空指针,防止误操作。
int* ptr = nullptr; // ptr是空指针,未指向任何有效地址
if (ptr != nullptr)
{
// 安全地使用指针
}
常见操作:
ptr == nullptr
:检查指针是否为空指针。
3. 常量指针
常量指针分为两种情况:
- 指向常量的指针:
const int* ptr;
指针指向的内容不能通过该指针修改。 - 指针常量:
int* const ptr;
指针本身是常量,不能指向其他地址。
const int* ptr = # // 指向常量的指针
//*ptr = 20; // 错误,不能通过ptr修改num i
nt* const ptr2 = # // 指针常量
//ptr2 = &num2; // 错误,ptr2不能指向其他地址
4. 指向指针的指针(多级指针)
指针也可以指向另一个指针,从而形成多级指针。二级指针(指向指针的指针)用于间接访问某个变量的指针。
int num = 10;
int* ptr = #
int** ptr2 = &ptr; // ptr2是指向ptr的指针
**ptr2 = 20; // 通过ptr2修改num的值为20
5. 函数指针
函数指针用于存储函数的地址,可以用来动态调用不同的函数。
int add(int a, int b)
{ return a + b; } i
nt (*funcPtr)(int, int) = &add; // 定义函数指针并初始化
int result = funcPtr(2, 3); // 通过函数指针调用add函数
6. 数组指针
数组指针是指向数组的指针,用于操作数组中的元素。
int arr[5] = {1, 2, 3, 4, 5};
int* ptr = arr; // 数组名本身就是指针,指向数组的首元素
for (int i = 0; i < 5; i++)
{
std::cout << *(ptr + i) << " "; // 访问数组元素
}
7. 智能指针
智能指针是C++11引入的一种指针类型,用于自动管理动态内存,防止内存泄漏。常见的智能指针包括std::unique_ptr
、std::shared_ptr
和std::weak_ptr
。
#include <memory>
std::unique_ptr<int> ptr(new int(10)); // 自动管理内存,离开作用域时释放内存
智能指针常见操作:
std::make_unique<T>()
:创建unique_ptr
。std::make_shared<T>()
:创建shared_ptr
。
常见指针操作
- 指针的算术运算:如
ptr++
、ptr--
可以移动指针,访问数组或连续内存中的其他元素。 - 比较指针:可以使用
==
、!=
、<
、>
等操作符来比较两个指针是否指向相同或不同的地址。
总结
指针是C++中一个强大且灵活的工具,但也容易出错,因此在使用时必须非常小心,确保每个指针都被正确初始化和使用,避免悬空指针、野指针等常见问题。
这里面要注意一个最特殊的指针类型,void* ,它很多场景必不可少,但是又问题多多。
在C++中,void*
是一种特殊的指针类型,称为“通用指针”或“未定类型指针”。它具有以下特性和操作:
1. 指向任意类型
void*
指针可以指向任意类型的对象,而不需要指定具体的类型。这使得void*
在某些场景下非常灵活,比如在实现通用数据结构或处理未知类型的数据时。
int intVar = 10;
double doubleVar = 20.5;
void* ptr; ptr = &intVar; // ptr可以指向int类型的变量
ptr = &doubleVar; // ptr也可以指向double类型的变量
2. 不能直接解引用
void*
指针是类型不确定的,因此不能直接解引用它以访问指向的值。要解引用void*
指针,必须首先将其转换为特定的类型指针。
int intVar = 10;
void* ptr = &intVar;
int* intPtr = static_cast<int*>(ptr); // 将void*转换为int*类型
std::cout << *intPtr << std::endl; // 现在可以解引用访问intVar的值
3. 指针算术操作不适用
由于void*
指针没有确定的类型,编译器无法确定指针步长,因此不允许对void*
指针执行指针算术操作(如ptr++
、ptr--
)。
void* ptr = nullptr;
// ptr++; // 错误,void*不支持指针算术操作
4. 类型安全问题
由于void*
不包含类型信息,使用它时需要小心。将void*
转换为错误的类型可能导致未定义行为或崩溃。这使得void*
使用起来非常灵活但也容易出错。
int intVar = 10;
void* ptr = &intVar; // 如果错误地将其转换为double*,可能会导致未定义行为 double* doublePtr = static_cast<double*>(ptr);
std::cout << *doublePtr << std::endl; // 未定义行为,可能崩溃
5. 常见使用场景
- 动态内存分配:如
malloc
函数返回的就是void*
类型,需要转换为特定类型的指针。 - 通用数据结构:如在实现通用的链表或队列时,
void*
可以用来存储不同类型的数据。 - 函数指针:某些函数可以接受
void*
作为参数,允许传递任意类型的数据。
void* malloc(size_t size); // malloc返回void*
int* intPtr = static_cast<int*>(malloc(sizeof(int) * 10)); // 转换为int*类型
6. void 与 nullptr*
void*
可以与nullptr
(或C中的NULL
)比较,表示指针为空(即不指向任何对象)。
void* ptr = nullptr;
if (ptr == nullptr)
{ std::cout << "ptr is null" << std::endl; }
总结
void*
指针是C++中功能强大的工具,用于处理类型不确定或通用的数据。然而,由于它的类型不安全性,在使用时需要特别小心,确保在解引用之前正确转换类型。void*
指针在某些低级编程和系统编程中广泛使用。
char* pStr=0;
short* pSnum=0;
int* pInt=0;
void* pvoid=0;
void** ppvoid=0;
pStr++; //移动一个字节
pSnum++;//移动2个字节
pInt++;//移动4个字节
pvoid++;//这个操作不允许,因为未知大小
ppvoid++;//移动四个字节
//通过这样的方式可以输出查看他们的便宜量:
printf("%d",pStr);
//另外:
struct test
{
char c;
short s;
int i;
void* pv;
};
//c在结构体里面占4字节,s也占4字节,i也一样,不要问为什么。
//如果有人问你不创建结构体的实例,如何查看他们的内存偏移
struct test* ptest=0;
int ipos = &(ptest->i); //可以这样做,虽然其实不建议,毕竟操作空指针本身就很危险