赞
踩
本篇文章记录我学习C++的指针内容,希望我的分享能给你带来不一样的收获!
目录
通过指针,可以简化一些 C++ 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的。每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。
- #include <iostream>
-
-
-
- using namespace std;
-
-
-
- int main ()
-
- {
-
- int var1;
-
- char var2[10];
-
-
-
- cout << "var1 变量的地址: ";
-
- cout << &var1 << endl;
-
-
-
- cout << "var2 变量的地址: ";
-
- cout << &var2 << endl;
-
-
-
- return 0;
-
- }
-
- 执行结果如下:
-
- var1 变量的地址: 0xbfebd5c0
-
- var2 变量的地址: 0xbfebd5b6
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:
type *var-name;
在这里,type 是指针的基类型,它必须是一个有效的 C++ 数据类型,var-name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:
int *ip; /* 一个整型的指针 */
double *dp; /* 一个 double 型的指针 */
float *fp; /* 一个浮点型的指针 */
char *ch; /* 一个字符型的指针 */
所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。
在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。
NULL 指针是一个定义在标准库中的值为零的常量。请看下面的程序:
实例
#include <iostream>
using namespace std;
int main ()
{
int *ptr = NULL;
cout << "ptr 的值是 " << ptr ;
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
ptr 的值是 0
在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。
如需检查一个空指针,可以使用 if 语句,如下所示:
if(ptr) /* 如果 ptr 非空,则完成 */
if(!ptr) /* 如果 ptr 为空,则完成 */
因此,如果所有未使用的指针都被赋予空值,同时避免使用空指针,就可以防止误用一个未初始化的指针。很多时候,未初始化的变量存有一些垃圾值,导致程序难以调试。
指针是一个用数值表示的地址。因此,可以对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-。
假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,让我们对该指针执行下列的算术运算:
ptr++
执行 ptr++ 后,指针 ptr 会向前移动 4 个字节,指向下一个整型元素的地址。这是由于指针算术运算会根据指针的类型和大小来决定移动的距离。在这种情况下,由于是一个 32 位整数指针,每个整数占据 4 个字节,因此 ptr++ 会将指针 ptr 向前移动 4 个字节,指向下一个整型元素的地址。
如果 ptr 指向一个地址为 1000 的字符,执行 ptr++ 指针 ptr 的值会增加,指向下一个字符元素的地址,由于 ptr 是一个字符指针,每个字符占据 1 个字节,因此 ptr++ 会将 ptr 的值增加 1,执行后 ptr 指向地址 1001。
指针算术运算的详细解析:
加法运算:可以对指针进行加法运算。当一个指针p加上一个整数n时,结果是指针p向前移动n个元素的大小。例如,如果p是一个int类型的指针,每个int占4个字节,那么p + 1将指向p所指向的下一个int元素。
减法运算:可以对指针进行减法运算。当一个指针p减去一个整数n时,结果是指针p向后移动n个元素的大小。例如,如果p是一个int类型的指针,每个int占4个字节,那么p - 1将指向p所指向的前一个int元素。
指针与指针之间的减法运算:可以计算两个指针之间的距离。当从一个指针p减去另一个指针q时,结果是两个指针之间的元素个数。例如,如果p和q是两个int类型的指针,每个int占4个字节,那么p - q将得到两个指针之间的元素个数。
指针与整数之间的比较运算:可以将指针与整数进行比较运算。可以使用关系运算符(如<、>、<=、>=)对指针和整数进行比较。这种比较通常用于判断指针是否指向某个有效的内存位置。
我们喜欢在程序中使用指针代替数组,因为变量指针可以递增,而数组不能递增,因为数组是一个常量指针。下面的程序递增变量指针,以便顺序访问数组中的每一个元素:
实例:
- #include <iostream>
-
-
-
- using namespace std;
-
- const int MAX = 3;
-
-
-
- int main ()
-
- {
-
- int var[MAX] = {10, 100, 200};
-
- int *ptr;
-
-
-
- // 指针中的数组地址
-
- ptr = var;
-
- for (int i = 0; i < MAX; i++)
-
- {
-
- cout << "Address of var[" << i << "] = ";
-
- cout << ptr << endl;
-
-
-
- cout << "Value of var[" << i << "] = ";
-
- cout << *ptr << endl;
-
-
-
- // 移动到下一个位置
-
- ptr++;
-
- }
-
- return 0;
-
- }
-
- 当上面的代码被编译和执行时,它会产生下列结果:
-
-
-
- Address of var[0] = 0xbfa088b0
-
- Value of var[0] = 10
-
- Address of var[1] = 0xbfa088b4
-
- Value of var[1] = 100
-
- Address of var[2] = 0xbfa088b8
-
- Value of var[2] = 200
同样地,对指针进行递减运算,即把值减去其数据类型的字节数,如下所示:
实例:
- #include <iostream>
-
-
-
- using namespace std;
-
- const int MAX = 3;
-
-
-
- int main ()
-
- {
-
- int var[MAX] = {10, 100, 200};
-
- int *ptr;
-
-
-
- // 指针中最后一个元素的地址
-
- ptr = &var[MAX-1];
-
- for (int i = MAX; i > 0; i--)
-
- {
-
- cout << "Address of var[" << i << "] = ";
-
- cout << ptr << endl;
-
-
-
- cout << "Value of var[" << i << "] = ";
-
- cout << *ptr << endl;
-
-
-
- // 移动到下一个位置
-
- ptr--;
-
- }
-
- return 0;
-
- }
-
-
-
- 当上面的代码被编译和执行时,它会产生下列结果:
-
-
-
- Address of var[3] = 0xbfdb70f8
-
- Value of var[3] = 200
-
- Address of var[2] = 0xbfdb70f4
-
- Value of var[2] = 100
-
- Address of var[1] = 0xbfdb70f0
-
- Value of var[1] = 10
指针可以用关系运算符进行比较,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。
下面的程序修改了上面的实例,只要变量指针所指向的地址小于或等于数组的最后一个元素的地址 &var[MAX - 1],则把变量指针进行递增:
实例:
- #include <iostream>
-
-
-
- using namespace std;
-
- const int MAX = 3;
-
-
-
- int main ()
-
- {
-
- int var[MAX] = {10, 100, 200};
-
- int *ptr;
-
-
-
- // 指针中第一个元素的地址
-
- ptr = var;
-
- int i = 0;
-
- while ( ptr <= &var[MAX - 1] )
-
- {
-
- cout << "Address of var[" << i << "] = ";
-
- cout << ptr << endl;
-
-
-
- cout << "Value of var[" << i << "] = ";
-
- cout << *ptr << endl;
-
-
-
- // 指向上一个位置
-
- ptr++;
-
- i++;
-
- }
-
- return 0;
-
- }
-
- 当上面的代码被编译和执行时,它会产生下列结果:
-
-
-
- Address of var[0] = 0xbfce42d0
-
- Value of var[0] = 10
-
- Address of var[1] = 0xbfce42d4
-
- Value of var[1] = 100
-
- Address of var[2] = 0xbfce42d8
-
- Value of var[2] = 200
从某种意义上来讲,指针和数组在一些情况下是可以相互互换的。一个指向数组开头的指针,可以通过使用指针的算术运算或数组索引来访问数组。
在C++中,指针和数组是相关但不完全相同的概念。下面是它们之间的一些比较:
1. 数组和指针的关系
- 数组是一系列相同类型的元素的集合,它们在内存中是连续存储的。
- 指针是一个变量,其值为另一个变量的地址。指针可以指向数组的首元素,也可以指向任意其他内存位置。
2. 数组名和指针
- 在大多数情况下,数组名会转换为指向数组首元素的指针。例如,对于数组
int arr[5];
,arr
可以被视为指向arr[0]
的指针。3. 初始化和赋值
- 数组的初始化:
int arr[5] = {1, 2, 3, 4, 5};
- 指针的初始化:
int* ptr = arr;
这里ptr
指向arr
的首元素。4. 指针与数组的访问
- 数组元素访问:使用下标访问
arr[i]
。- 指针访问数组元素:可以使用指针进行数组元素的访问,比如
*(arr + i)
或者ptr[i]
(等效于*(ptr + i)
)。5. 数组的性质
- 数组是常量指针:数组名本身在大多数情况下是一个常量指针,不能被重新赋值。
6. 指针的灵活性
- 指针的灵活性:指针可以指向数组的任意位置,也可以通过指针进行遍历和修改数组元素。
7. 指针与数组的区别
- 大小和维度:数组具有固定的大小和维度,而指针没有固定大小,可以指向不同大小的内存块。
- 内存分配:数组在声明时需要分配固定大小的内存空间,而指针可以动态分配或指向已分配的内存块。
下面举一个实例:
- #include <iostream>
-
- int main() {
- int arr[5] = {1, 2, 3, 4, 5};
- int* ptr = arr; // ptr指向arr的首元素
-
- // 使用数组下标访问元素
- for (int i = 0; i < 5; ++i) {
- std::cout << arr[i] << " ";
- }
- std::cout << std::endl;
-
- // 使用指针访问元素
- for (int i = 0; i < 5; ++i) {
- std::cout << *(ptr + i) << " ";
- }
- std::cout << std::endl;
-
- return 0;
- }
指针数组是一个数组,其中的每一个元素都是指针。每个指针可以指向数组、字符串、函数或者其他数据类型的内存地址。这种数据结构在C和C++等编程语言中经常被使用。
int *ptrArray[5]; // 声明一个包含5个指向int类型数据的指针数组
指针数组可以通过循环或者手动赋值的方式进行初始化
- int a = 10, b = 20, c = 30, d = 40, e = 50;
- int *ptrArray[5] = {&a, &b, &c, &d, &e}; // 初始化指针数组
可以使用数组索引来访问指针数组的元素,每个元素是一个指针,可以通过解引用操作符 * 来获取指向的值。
printf("%d\n", *ptrArray[0]); // 输出指针数组第一个元素指向的值
也可以动态分配内存给指针数组,这在需要根据程序运行时情况动态调整数组大小时很有用。
- int **ptrArray;
- int size = 5;
- ptrArray = (int **)malloc(size * sizeof(int *)); // 分配存储指针的内存空间
ptrArray = (int **)malloc(size * sizeof(int *));
malloc(size * sizeof(int *))
:malloc
是 C 标准库中的函数,用于动态分配内存。sizeof(int *)
是指针类型int *
的大小(在大多数系统上通常是4字节或8字节,取决于系统的位数),size
是要分配的指针的数量。因此,size * sizeof(int *)
表示要分配的总字节数,即存储size
个指针所需的总内存空间。
(int **)
: 这是一种类型转换,将malloc
返回的通用指针void *
转换为int **
类型的指针。int **
表示指向指针的指针,因此它用于指向指针数组的指针。所以,整体来说,这行代码的意思是:动态分配一个可以存储
size
个int
类型指针的内存空间,并将其地址赋值给ptrArray
,这样ptrArray
就成为了一个指向int
类型指针数组的指针。
如果使用了动态分配内存的指针数组,在使用完毕后需要进行释放内存,以避免内存泄漏。
free(ptrArray); // 释放内存
- #include <stdio.h>
-
- int main() {
- int a = 10, b = 20, c = 30, d = 40, e = 50;
- int *ptrArray[5] = {&a, &b, &c, &d, &e}; // 初始化指针数组
-
- // 访问指针数组元素并输出
- for (int i = 0; i < 5; i++) {
- printf("%d ", *ptrArray[i]);
- }
- printf("\n");
-
- return 0;
- }
指向指针的指针是指一个指针变量,它存储的是另一个指针变量的地址。在C和C++中,指针本身也是一种变量,它存储的是内存地址,而指向指针的指针则是存储指针变量的地址的变量。
下面举一个简单的例子来说明一下:
- #include <stdio.h>
-
- int main() {
- int x = 10;
- int *ptr1 = &x; // ptr1 指向 x 的地址
- int **ptr2 = &ptr1; // ptr2 指向 ptr1 的地址
-
- printf("x = %d\n", x); // 输出 x 的值
- printf("*ptr1 = %d\n", *ptr1); // 输出 ptr1 指向的值,即 x 的值
- printf("**ptr2 = %d\n", **ptr2); // 输出 ptr2 指向的值,即 ptr1 指向的值,即 x 的值
-
- return 0;
- }
在这个例子中:
ptr1
是一个指向x
的指针,它存储了x
的地址。ptr2
是一个指向ptr1
的指针,它存储了ptr1
的地址。**ptr2
指的是通过ptr2
找到ptr1
,再通过ptr1
找到x
,因此输出的是x
的值。指向指针的指针在某些情况下很有用,特别是在函数中传递指针的地址以便能够修改指针指向的内容时。
通过传递指针给函数来实现对函数外部变量的引用或者修改。这种方式在函数调用时不会对原始数据进行拷贝,而是直接操作原始数据的地址。以下是传递指针给函数的基本方法。
1、函数声明和定义
- // 函数声明
- void modifyValue(int* ptr);
-
- // 函数定义
- void modifyValue(int* ptr) {
- *ptr = 100; // 修改指针所指向的变量的值为100
- }
2、调用函数并且传递指针
- int main() {
- int value = 10;
- int* ptr = &value; // 指针ptr指向value的地址
-
- // 调用函数并传递指针
- modifyValue(ptr);
-
- // 打印修改后的值
- std::cout << "Modified value: " << value << std::endl;
-
- return 0;
- }
3、解析
- 在
main
函数中创建了一个整型变量value
,并将其地址赋给指针ptr
。modifyValue
函数接受一个指针作为参数,通过该指针可以修改原始数据的值。- 在调用
modifyValue
函数时,将指针ptr
作为参数传递给函数,函数内部通过解引用指针来修改变量value
的值。- 打印修改后的值,可以看到
value
的值已经被修改为100。注意事项
- 在使用指针传递参数时,要确保指针不为NULL,否则可能会导致未定义的行为或错误。
- 使用指针传递参数时,要注意指针所指向的内存区域的生命周期,确保在函数使用期间该内存区域是有效的。
- 当需要在函数内部修改函数外部变量的值时,传递指针是一种有效的方法,但需要注意函数的副作用。
在C++中,函数可以返回指针以提供对动态分配内存或函数外部变量的访问。返回指针的函数通常用于以下情况:
动态内存分配:函数可以在堆上动态分配内存,并返回指向该内存的指针,允许调用者在函数外部访问分配的内存。
函数外部变量的访问:函数可以返回指向函数外部变量的指针,以便在函数外部修改该变量的值。
1、返回指向动态分配内存的指针
- #include <iostream>
-
- int* createArray(int size) {
- int* arr = new int[size]; // 在堆上动态分配内存
- for (int i = 0; i < size; ++i) {
- arr[i] = i * 2; // 初始化数组元素
- }
- return arr; // 返回指针指向分配的内存
- }
-
- int main() {
- int* ptr = createArray(5); // 调用函数并接收返回的指针
-
- // 使用返回的指针访问动态分配的内存
- for (int i = 0; i < 5; ++i) {
- std::cout << ptr[i] << " ";
- }
- std::cout << std::endl;
-
- // 释放动态分配的内存
- delete[] ptr;
-
- return 0;
- }
2、返回指向函数外部变量的指针
- #include <iostream>
-
- int* findLarger(int a, int b) {
- if (a > b) {
- return &a; // 返回指向a的指针
- } else {
- return &b; // 返回指向b的指针
- }
- }
-
- int main() {
- int x = 5, y = 10;
- int* larger = findLarger(x, y); // 调用函数并接收返回的指针
-
- // 使用返回的指针修改函数外部变量的值
- *larger = 100;
- std::cout << "x: " << x << std::endl; // 输出修改后的x的值
- std::cout << "y: " << y << std::endl; // 输出修改后的y的值
-
- return 0;
- }
注意事项
- 返回指针的函数必须确保返回的指针在函数外部仍然有效。在第一个例子中,返回的指针指向动态分配的内存,因此在使用完指针后需要负责释放内存。
- 当函数返回指向函数外部变量的指针时,要确保函数外部变量的生命周期足够长,以免在指针使用期间变量被销毁而导致悬挂指针(dangling pointer)问题。
- 在使用返回指针的函数时,要注意对返回的指针进行空指针检查,以确保指针的有效性。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。