对于刚入门编程的人来说,最难理解的有两个,一个是结构体,另一个便是指针。这一节我们就来仔细讲解一下指针。
一.什么是指针?
我们学习过很多变量类型,比如int,char等类型。而指本质上也是一个变量类型,它用于存储另一个变量的内存地址。
在内存中,每一个字节都有属于自己的地址,如果我们想获取这个字节的地址,我们就可以用指针进行储存。指针也是一个变量,创建指针也要占用内存,在32位机器下,指针大小为4个字节,而在64位机器下,指针大小为8个字节。
二.指针的初始化
int *p = &a;
例如这个语句,p这个变量类型就是指针,代表着它存储着a变量的地址,并且变量是int类型的。而&a代表着对a取地址。
三.解引用
解引用代表着对指针变量进行解析,所得到的变量是指针指向的变量的数值。
printf("%d\n", *p);
这个语句就是打印p锁指向的内容的值,*p就代表对p指针解引用。我们可以这样理解,p是int*类型的,那么*p就是int类型的。
四.指针的运算
指针可以进行加法和减法运算,以指向数组中的不同元素或不同的位置。
int arr[5] = {1,2,3,4,5};
int *p = arr;
此时,p指向的就是数组arr的首地址(第一个元素)。而此时我们可以对p进行++操作,使得p指向下一个元素。但是要注意指针越界的问题。
当然,我们也可以对两个指针进行运算操作。
int arr[5] = {1,2,3,4,5};
int *p = arr;
int *q=arr+4;
int num =q-p;
其中num代表的就是两个指针直接相差了多少个元素。
五.二级指针
我们说过,指针的本质是一个变量类型,那么存放指针的是什么呢?没错就是二级指针。
int a = 10;
int* p1 = &a;
int** p2 = &p1;
其中,p1就是一级指针,p2就是二级指针。以此类推,我们可以有三级、四级、...、n级,本质上其实都是变量类型。
六.空指针,野指针问题
首先我们要明白空指针和野指针分别是什么。并且我们来聊聊他们的用途。
空指针:
- 定义:空指针是指不指向任何有效对象的指针。它通常被初始化为NULL(在C/C++中可以使用
nullptr
来表示)。 - 用途:空指针常用于指示指针当前不指向任何内存地址,或者用于判断指针是否有效。通过检查指针是否为NULL,可以避免对无效内存的访问。
野指针:
- 定义:野指针是指指向已经被释放或不再有效的内存地址的指针。换句话说,野指针的指向对象已经不存在,使用这样的指针将导致未定义行为。
- 原因:常见的原因包括:
- 对动态分配的内存使用
free
或delete
后未将指针置为NULL; - 返回局部变量的地址;
- 从某个作用域中退出后,指向该作用域内的对象。
int *ptr = (int*)malloc(sizeof(int)); // 动态分配内存
*ptr =10; // 使用指针
free(ptr); //释放内存 // ptr现在是一个野指针,因为指向已释放的内存
此时ptr就是野指针。
区别总结- 空指针是安全的,但野指针是不安全的。
- 空指针表示不指向任何对象,而野指针则指向已失效的内存地址。
处理指针时,应谨慎使用,并确保在释放内存后将指针置为NULL,以防止出现野指针问题。
七.指针数组与数组指针
1.指针数组
我们学习过字符数组,整型数组,指针数组其实就是数组内容为指针的数组。
数组arr3就是一个指针数组。
2.数组指针
我们知道,整型指针是指向整型的指针,字符指针是指向字符的指针,那么数组指针应该就是指向数组的指针了。
int (*p)[10] = &arr;
这里的p就是一个数组指针了。
八.函数指针以及回调函数
1.函数指针
函数指针是指向函数的指针,能够存储函数的地址,从而使得程序能够通过指针调用函数。函数指针在C和C++中广泛使用,特别是在需要回调、实现多态或动态函数调用的场景。
函数指针的定义通常包含函数的返回类型、参数类型以及指针符号。例如,如果有一个返回int
,并接受两个int
参数的函数 ,那么它的函数指针的类型可以定义为:
int myFunction(int, int)
函数指针可以通过函数名来进行赋值。函数名代表了函数的地址。如下:
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
void compute(int (*operation)(int, int), int x, int y) {
printf("Result: %d\n", operation(x, y));
}
int main() {
int (*funcPtr)(int, int);
funcPtr = add;
compute(funcPtr,5,3); // 输出:Result:8
funcPtr = subtract;
compute(funcPtr,5,3); // 输出:Result:2 return0;
}
2.回调函数
回调函数是指通过函数指针向其他函数传递的函数。被传递的函数称为回调函数,它可以在某个特定事件发生时由另一个函数调用。
typedef int (*Callback)(int, int);
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
void performOperation(Callback callback, int x, int y) {
int result = callback(x, y);
printf("Result: %d\n", result);
}
int main() {
performOperation(add,5,3); // 输出:Result:8
performOperation(multiply,5,3); // 输出:Result:15 return0;
}
我们先定义了一个回调函数类型,我们在performOperation里设置一个回调函数,我们在main函数可以任意传递类型匹配的函数,在performOperation里实现,则上述代码中的callback函数就是回调函数。
回调函数是一种灵活的编程模式,使得代码的可重用性和扩展性显著增强。无论是在异步操作、事件处理还是自定义算法中,回调函数都扮演着重要的角色。通过合理地使用回调函数,可以使得程序设计更具可读性和灵活性。