C++ 指针介绍
指针是C++语言中非常重要的概念,它提供了对内存中数据的直接访问方式。指针存储了一个变量的内存地址,可以通过指针来访问和操作该变量。
以下是指针的一些基本介绍:
- 定义指针:在C++中,可以使用
*
(星号)来声明一个指针变量。例如,int* ptr;
声明了一个名为ptr
的指向整数的指针变量。需要注意的是,指针变量在声明时应初始化为一个有效的内存地址。 - 获取变量地址:使用取地址运算符
&
可以获取一个变量的内存地址。例如,int num = 10; int* ptr = #
将ptr
指向num
的地址。 - 解引用指针:使用解引用运算符
*
可以访问指针所指向的变量的值。例如,int num = 10; int* ptr = # std::cout << *ptr;
会输出10
,即ptr
指向的num
的值。 - 动态内存分配:使用
new
关键字可以在程序运行时动态地分配内存空间。例如,int* ptr = new int;
会在堆上分配一个整数大小的内存,并将其地址赋给ptr
。 - 删除动态分配的内存:使用
delete
关键字可以释放通过new
分配的动态内存。例如,delete ptr;
会释放由ptr
指向的内存。 - 空指针:空指针指向内存地址0,表示指针不指向任何有效的对象。可以使用
nullptr
或NULL
来初始化或比较指针变量是否为空。 - 指针算术:指针可以进行算术运算,如指针的加法、减法等。这些运算会根据指针类型的大小来改变指针指向的地址。
指针在C++中非常灵活和强大,可以用于许多方面,如数组访问、动态内存管理、函数交互等。然而,需要小心处理指针,因为不正确的指针操作可能导致程序崩溃或内存泄漏等问题。
C++指针声明和使用
当声明和使用C++指针时,可以按照以下方式进行:
- 声明指针:使用
*
来声明一个指针变量,表示该变量将存储某个类型的内存地址。例如,声明一个指向整数的指针:
int* ptr;
- 初始化指针:可以使用取地址运算符
&
来获取一个变量的地址,并将其赋值给指针变量。例如,将一个整数变量的地址赋值给指针:
int num = 10; int* ptr = #
- 解引用指针:使用解引用运算符
*
来访问指针所指向的变量的值。例如,输出指针所指向的整数变量:
std::cout << *ptr;
- 动态分配内存:使用
new
关键字可以在程序运行时动态地分配内存。例如,动态分配一个整数大小的内存,并使用指针来引用它:
int* ptr = new int;
- 释放内存:使用
delete
关键字释放通过new
分配的内存。例如,释放先前动态分配的内存:
delete ptr;
下面是一个完整的示例,演示了指针的声明、初始化、解引用和动态内存分配等操作:
#include <iostream> int main() { int num = 20; int* ptr = # std::cout << "Pointer value: " << *ptr << std::endl; std::cout << "Memory address: " << ptr << std::endl; int* dynamicPtr = new int; *dynamicPtr = 30; std::cout << "Dynamic pointer value: " << *dynamicPtr << std::endl; delete dynamicPtr; return 0; }
这个示例中,首先定义了一个整数变量num
和一个指向整数的指针ptr
,并输出了指针所指向的值和地址。接下来,使用new
动态分配了一个整数大小的内存,并将其地址赋值给dynamicPtr
,然后输出了该指针所指向的值。最后,通过delete
释放了动态分配的内存。
C++的指针常量和常量指针
C++中的指针常量和常量指针是两种不同类型的指针,它们具有不同的特性。下面分别举例说明:
- 指针常量(Pointer to Constant):指针常量是指指针本身是常量,即指针所指向的地址不能变化,但它所指向的值可以修改。声明一个指针常量时需要在
*
前加上const
关键字。
int num = 10; const int* ptr = #
在这个例子中,ptr
是一个指向整数常量的指针。这意味着ptr
可以指向不同的整数,但无法通过ptr
来修改所指向的整数的值。以下操作是合法的:
int anotherNum = 20; ptr = &anotherNum; // 指针可以指向不同的整数
但以下操作是不合法的,会导致编译错误:
*ptr = 30; // 无法通过指针来修改所指向的整数值
- 常量指针(Constant Pointer):常量指针是指指针本身可变,即指针所指向的地址可以变化,但它所指向的值不可修改。在声明一个常量指针时需要在指针名前加上
const
关键字。
int num = 10; int* const ptr = #
在这个例子中,ptr
是一个常量指针,它指向一个整数。这意味着ptr
所指向的地址不能改变,一旦指向了一个地址,就无法通过ptr
来指向其他地址。以下操作是合法的:
*ptr = 20; // 可以通过指针来修改所指向的整数的值
但以下操作是不合法的,会导致编译错误:
int anotherNum = 30; ptr = &anotherNum; // 无法更改指针所指向的地址
通过理解指针常量和常量指针的概念和用法,可以更好地控制和管理指针在程序中的行为。在实际应用中,根据需求选择使用适当类型的指针可以提高代码的可读性和安全性。
C++指针和数组
在C++中,指针和数组可以紧密结合使用。指针可以用于访问和操作数组的元素,通过指针可以实现对数组的遍历、修改和传递等操作。以下是一些示例:
- 声明指向数组的指针:可以使用
*
声明一个指向数组的指针。
int arr[5] = {1, 2, 3, 4, 5}; int* ptr = arr;
在这个例子中,ptr
是一个指向整数数组arr
的指针。指针ptr
指向数组的第一个元素。
- 使用指针遍历数组:可以通过指针来遍历数组,以访问和修改数组的元素。
for (int i = 0; i < 5; i++) { std::cout << *(ptr + i) << " "; // 输出数组元素的值
这里使用指针ptr
遍历数组arr
,*(ptr + i)
表示指针移动到当前索引位置的元素。通过循环输出数组的元素。
- 使用指针修改数组的值:指针也可以用于修改数组元素的值。
*(ptr + 2) = 10;
这行代码会将数组中索引为2的元素的值修改为10。
- 将指针作为函数参数:指针可以作为函数的参数,用于传递数组。
void printArray(int* arr, int size) { for (int i = 0; i < size; i++) { std::cout << arr[i] << " "; } } int main() { int arr[5] = {1, 2, 3, 4, 5}; printArray(arr, 5); return 0; }
在这个例子中,printArray
函数接受一个指向整数的指针arr
和数组大小size
作为参数。在main
函数中,将数组arr
和数组大小传递给printArray
函数来打印数组的元素。
通过指针和数组的结合使用,可以更灵活地操作和处理数组数据。需要注意的是,在使用指针访问数组时要确保不越界,以避免错误和未定义行为的发生。
C++ 指针,数组分别做函数参数
在C++中,指针和数组作为函数参数时有多种情况。下面举例说明一些常见情况:
- 将指针作为函数参数:
void modifyValue(int* ptr) { *ptr = 10; } int main() { int num = 5; modifyValue(&num); std::cout << num; // 输出:10 return 0; }
在这个例子中,modifyValue
函数接受一个指向整数的指针ptr
。通过引用传递(或传递指针的地址),函数可以修改指针指向的变量的值。
- 将数组作为函数参数(指针形参):
void modifyArray(int* arr, int size) { for (int i = 0; i < size; i++) { arr[i] *= 2; } } int main() { int arr[5] = {1, 2, 3, 4, 5}; modifyArray(arr, 5); for (int i = 0; i < 5; i++) { std::cout << arr[i] << " "; // 输出:2 4 6 8 10 } return 0; }
在这个例子中,modifyArray
函数接受一个指向整数的指针arr
和数组大小size
作为参数。通过指针形参,函数可以修改数组元素的值。
- 将数组作为函数参数(使用数组形参):
void modifyArray(int arr[], int size) { for (int i = 0; i < size; i++) { arr[i] *= 2; } } int main() { int arr[5] = {1, 2, 3, 4, 5}; modifyArray(arr, 5); for (int i = 0; i < 5; i++) { std::cout << arr[i] << " "; // 输出:2 4 6 8 10 } return 0; }
这个例子与前一个例子相似,不同之处在于这里使用了数组形参,函数参数中的int arr[]
实际上是一个指针,它和第二个例子中的int* arr
形式是等价的。
无论是将指针还是数组作为函数参数,都可以在函数内部修改它们所指向的数据。需要注意的是,在函数内部修改数组时,要确保不越界访问数组的元素,以避免引发错误或未定义行为。
C++ 指针在参数传递中的各种情况
在C++中,指针在参数传递中有以下几种情况:
- 将指针作为参数传递:
void modifyPointer(int* ptr) { int num = 10; ptr = # // 修改指针指向的地址 } int main() { int* ptr = nullptr; modifyPointer(ptr); std::cout << ptr; // 输出:nullptr return 0; }
在这个例子中,modifyPointer
函数接受一个指向整数的指针ptr
作为参数。函数内部修改了指针的值(即指向了另一个地址),但是这个改变不影响函数外部的指针。
- 将指针的引用作为参数传递:
void modifyPointer(int*& ptr) { int num = 10; ptr = # // 修改指针的引用 } int main() { int* ptr = nullptr; modifyPointer(ptr); std::cout << ptr; // 输出:0x[某个地址] return 0; }
在这个例子中,modifyPointer
函数接受一个指向整数的指针的引用ptr
作为参数。通过将指针的引用传递给函数,函数可以修改指针本身的值,这会影响函数外部的指针。
- 将指向指针的指针作为参数传递:
void modifyDoublePointer(int** pptr) { int num = 10; *pptr = # // 修改指针的指针 } int main() { int* ptr = nullptr; int** pptr = &ptr; modifyDoublePointer(pptr); std::cout << *pptr; // 输出:0x[某个地址] return 0; }
在这个例子中,modifyDoublePointer
函数接受一个指向指针的指针pptr
作为参数。通过间接访问,函数可以修改指针指向的地址,从而影响函数外部的指针。
指针在参数传递中非常灵活,可以根据需求选择合适的方式来传递指针,以实现对指针自身或指向的数据进行修改。需要注意的是,在修改指针指向的数据时,要注意避免访问已经释放的内存或导致未定义行为的操作。
C++ 指针错误用法
下面是一些常见的C++指针错误用法的例子:
- 未初始化指针:
int* ptr; *ptr = 10; // 未初始化的指针无效,会导致未定义行为
这里ptr
是一个未初始化的指针,对未初始化的指针进行解引用操作会导致未定义行为,可能会引发程序崩溃。
- 使用空指针:
int* ptr = nullptr; *ptr = 10; // 空指针无效,会导致未定义行为
这个例子中,将整型指针ptr
设置为nullptr,然后试图通过解引用操作给空指针赋值,这也会导致未定义行为。
- 访问超出范围的指针:
int arr[5] = {1, 2, 3, 4, 5}; int* ptr = &arr[0]; *(ptr + 6) = 10; // 越界访问,会导致未定义行为
在这个例子中,指针ptr
指向数组arr
的第一个元素,在试图访问存储在第7个位置上的元素时,超出了数组的范围,这会导致未定义行为。
- 错误释放内存:
int* ptr = new int(5); delete ptr; *ptr = 10; // 已删除的内存再次使用,会导致未定义行为
这个例子中,使用new
动态分配了一个整型对象,并用指针ptr
进行指向。之后通过delete
释放了该内存,并尝试重复使用已被释放的内存空间,这会导致未定义行为。
- 悬空指针:
int* getPtr() { int num = 5; return # // 返回了函数作用域内的局部变量的地址 } int main() { int* ptr = getPtr(); *ptr = 10; // 悬空指针,会导致未定义行为 return 0; }
这个例子中,在函数getPtr
中返回局部变量num
的地址,当指针ptr
尝试访问这个地址时,访问的是一个已经超出作用域的局部变量,这也会导致未定义行为。
避免这些指针错误的发生,需要注意在使用指针时,确保指针是有效的、已初始化的,并正确地管理动态分配的内存。