指针
指针(Pointer)是C语言中的一种特殊数据类型,用于存储变量的内存地址。它允许直接访问和操作内存中的数据。
在C语言中,每个变量都在内存中有一个唯一的地址。指针变量用来存储这个地址,并可以通过指针来访问和修改存储在该地址处的数据。
指针的声明使用*
符号,例如:
int *ptr;
这里的ptr
是一个指向整数类型的指针变量。它可以存储一个整数变量的地址。
要获取变量的地址,可以使用取地址运算符&
,例如:
int num = 10;
int *ptr = #
这里的&num
表示变量num
的地址,将其赋值给指针ptr
。
要访问指针指向的值,可以使用解引用运算符*
,例如:
int value = *ptr;
这里的*ptr
表示指针ptr
所指向的值,将其赋值给变量value
。
指针在C语言中具有许多重要的用途,包括:
- 动态内存分配:通过指针可以在运行时动态地分配和释放内存。
- 数组访问:指针可以用于遍历和操作数组中的元素。
- 函数传递:指针可以用于在函数之间传递数据,以便在函数内部修改原始数据。
- 数据结构:指针可以用于创建和操作复杂的数据结构,如链表和树。
使用指针需要小心,因为错误的使用指针可能导致内存访问错误和程序崩溃。因此,在使用指针时应该确保指针指向有效的内存地址,并正确地管理内存的分配和释放。
野指针
1.什么是野指针?
野指针(Wild Pointer)是指一个指针变量,它存储了一个无效的、未被初始化或已经释放的内存地址。使用野指针是一种编程错误,可能导致程序的不可预测行为和潜在的安全问题。
野指针通常出现在以下情况下:
-
未初始化的指针:当一个指针变量被声明但没有被初始化时,它的值是不确定的,可能是一个随机的内存地址。如果在使用这个指针之前没有为它分配有效的内存地址,就会产生野指针。
int *ptr; // 未初始化的指针 *ptr = 10; // 错误!野指针 非法访问内存
-
已释放的内存:如果一个指针在释放了内存后仍然保持对该内存的引用,那么这个指针就成为了野指针。
int *ptr = malloc(sizeof(int)); // 使用ptr分配和操作内存 free(ptr); // 释放内存 *ptr = 10; // 错误!野指针
-
超出作用域的指针:当一个指针变量超出了其所在的作用域范围,它将成为一个野指针,因为它指向的内存可能已经被释放或重新分配给其他变量。
int *getPointer() { int num = 10; int *ptr = # return ptr; // 返回局部变量的指针 } int *ptr = getPointer(); // ptr成为野指针
4.越界访问
int arr[10] = {0}; int* p = arr; int i = 0; for(i = 0;i <= 10;i++) { *p = i; p++; }
使用野指针可能导致程序崩溃、数据损坏或安全漏洞。为了避免野指针问题,应该始终确保指针变量指向有效的内存地址,避免在指针未初始化或已释放的情况下使用它们,并在超出作用域范围之前及时释放指针所引用的内存。
2.如何避免野指针
要避免野指针问题,可以采取以下几个方法:
-
初始化指针:在声明指针变量时,立即将其初始化为一个有效的值或将其设置为NULL。这样可以确保指针不会成为野指针。
int *ptr = NULL; // 当不知道应该初始化什么值时,直接初始化为NULL
-
及时释放指针:在使用完指针引用的内存后,及时释放该内存并将指针设置为NULL。这样可以避免在使用已释放的内存时产生野指针。
int *ptr = malloc(sizeof(int)); // 使用ptr分配和操作内存 free(ptr); // 释放内存 ptr = NULL; // 将指针设置为NULL
-
避免超出作用域问题:确保指针变量的生命周期与其所引用的内存的生命周期相匹配。不要在指针超出其作用域后继续使用它。
int *getPointer() { int num = 10; int *ptr = # return ptr; // 错误!返回局部变量的指针 } int *ptr = getPointer(); // ptr成为野指针
-
检查指针的有效性:在使用指针之前,始终检查指针是否为NULL。这样可以避免在使用无效指针时产生野指针。
int *ptr = NULL; // 检查指针是否为NULL if (ptr != NULL) { // 使用ptr访问内存 }
-
小心使用指针操作:当使用指针进行内存操作时,确保指针指向有效的内存区域,并遵循正确的指针操作规则,以避免访问无效内存导致野指针问题。
int arr[5] = {1, 2, 3, 4, 5}; int *ptr = &arr[0]; // 遍历数组并访问元素 for (int i = 0; i < 5; i++) { printf("%d ", *ptr); ptr++; }
通过遵循上述方法,可以减少野指针问题的发生。此外,良好的编码实践、仔细的内存管理和代码审查也是确保指针安全的重要手段。
3.指针运算
指针运算是指对指针变量进行数学运算或逻辑运算的操作。在C语言中,指针运算可以用来实现对数组的遍历、字符串操作以及动态内存分配等功能。下面是一些常见的指针运算操作:
-
指针的加法和减法:可以对指针进行加法和减法运算,其中加法运算会使指针向后移动若干个元素的距离,减法运算会使指针向前移动若干个元素的距离。这里的元素距离是指指针所指向类型的大小。
int arr[5] = {1, 2, 3, 4, 5}; int *ptr = arr; // 指向数组的第一个元素 ptr++; // 指针向后移动一个元素的距离 printf("%d\n", *ptr); // 输出2 ptr--; // 指针向前移动一个元素的距离 printf("%d\n", *ptr); // 输出1
-
指针的比较:可以对指针进行比较运算,比较的结果可以用于判断两个指针的相对位置关系。
int arr[5] = {1, 2, 3, 4, 5}; int *ptr1 = &arr[1]; int *ptr2 = &arr[3]; if (ptr1 < ptr2) { printf("ptr1 is before ptr2\n"); } else if (ptr1 > ptr2) { printf("ptr1 is after ptr2\n"); } else { printf("ptr1 and ptr2 are the same\n"); }
-
指针与整数的加法和减法:可以将指针与整数进行加法和减法运算,这样可以使指针向后或向前移动若干个元素的距离。
int arr[5] = {1, 2, 3, 4, 5}; int *ptr = arr; // 指向数组的第一个元素 ptr = ptr + 2; // 指针向后移动两个元素的距离 printf("%d\n", *ptr); // 输出3 ptr = ptr - 1; // 指针向前移动一个元素的距离 printf("%d\n", *ptr); // 输出2
4.指针-指针:指针-指针得到是两个指针之间元素的个数,指针和指针相减的前提是两个指针指向同一块空间
int main() { int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; printf("%d\n",&arr[9] - &arr[0]); return 0; }
拓展:指针-指针,运算字符长度
#include<stdio.h> #include<string.h> int my_strlen(char* str) { char* strat = str; while(*str != '\0') //判断\0 { str++; } return str - strat; } int main() { //strlen 求字符串长度 int len = my_strlen("abc"); printf("%d\n",len); return 0; }
5.指针的关系运算
指针的关系运算用于比较两个指针的相对位置关系。在C语言中,指针的关系运算符包括以下几种:-
相等性比较(==):判断两个指针是否指向同一个内存地址。
int a = 5; int *ptr1 = &a; int *ptr2 = &a; if (ptr1 == ptr2) { printf("ptr1 和ptr2 指向同一个地址\n"); } else { printf("ptr1 和ptr2 指向不同的地址\n"); }
-
不等性比较(!=):判断两个指针是否指向不同的内存地址。
int a = 5; int b = 10; int *ptr1 = &a; int *ptr2 = &b; if (ptr1 != ptr2) { printf("ptr1 和ptr2 指向同一个地址\n"); } else { printf("ptr1 和ptr2 指向不同的地址\n"); }
-
-
大小比较(<、>、<=、>=):判断两个指针指向的内存地址的大小关系。这里的大小关系是基于指针所指向的内存地址在内存中的排列顺序。
int arr[5] = {1, 2, 3, 4, 5}; int *ptr1 = &arr[1]; int *ptr2 = &arr[3]; if (ptr1 < ptr2) { printf("ptr1 is before ptr2\n"); } else if (ptr1 > ptr2) { printf("ptr1 is after ptr2\n"); } else { printf("ptr1 and ptr2 point to the same address\n"); }
注:C语言的标准规定:
允许指向数值元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指针第一个元素之前的那个内存位置的指针进行比较。for (vp = &values[5 - 1]; vp > &values[0];) { *--vp = 0; }
这种写法不建议!
for (vp = &values[5 - 1]; vp > &values[0];vp--) { *vp = 0; }
需要注意的是,指针的关系运算仅在两个指针指向同一个数组或同一个对象时才有意义。对于两个指针指向不同的内存区域,或者指向不同类型的对象,进行关系运算的结果是未定义的。此外,指针的关系运算也受到指针类型的限制,不同类型的指针之间不能进行大小比较。
需要注意的是,指针运算应谨慎使用,确保指针指向有效的内存区域,避免越界访问或产生野指针。此外,指针运算也受到指针类型的限制,不同类型的指针具有不同的步长。