一. 野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
1. 野指针成因
- 指针未初始化
#include <stdio.h> int main() { int *p;//局部变量指针未初始化,默认为随机值 *p = 20; return 0; }
- 指针越界访问
#include <stdio.h> int main() { int arr[10] = {0}; int *p = arr; int i = 0; for(i=0; i<=11; i++) { //当指针指向的范围超出数组arr的范围时,p就是野指针 *(p++) = i; } return 0; }
- 指针指向的空间释放
- 这里放在指针进阶篇中动态内存开辟的时候讲解
2. 如何规避野指针
1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放,及时置NULL
4. 避免返回局部变量的地址
5. 指针使用之前检查有效性
第三种情况的使用例子
#include <stdio.h> int main() { int *p = NULL;//在使用前先把指针置空防止为释放指针之前指向的空间 //.... int a = 10; p = &a; if(p != NULL) { *p = 20; } return 0; }
二. 指针运算
1.指针± 整数
#define m 5 float values[m]; float* vp; //指针+-整数;指针的关系运算 for (vp = &values[0]; vp < &values[m];) { *vp++ = 0;//把values数组中每个元素都初始化为0,这里指针++表示地址后移 }
2.指针-指针
//计算字符串的长度 int my_strlen(char *s) { char *p = s; while(*p != '\0' ) p++; return p-s;//当p中读取到\0时说明该字符串总长度为此时的地址p-首元素地址s }
3.指针的关系运算
for(vp = &values[m]; vp > &values[0];) { *--vp = 0;//由于是前置--,最后会与第一元素之前的元素比较 }
- 上面这段代码有比较明显的缺点,我们来简化一下。
for(vp = &values[m-1]; vp >= &values[0];vp--) { *vp = 0; }
- 实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行。
- 标准规定:
- 允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
三. 指针和数组
- 我们先通过一个例子来建立一下数组与指针之间的联系
#include <stdio.h> int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,0}; printf("%p\n", arr);//打印地址 printf("%p\n", &arr[0]); return 0;
- 我们可以发现两者的地址是相同的
- 可见数组名和数组首元素的地址是一样的。
- 结论:
- 数组名表示的是数组首元素的地址。(2种情况除外,在之前的博客中讲过,链接如下:【C语言初阶】带你玩转C语言中的数组,并逐步实现冒泡排序,三子棋,扫雷)
- 那么这样写代码是可行的:
int arr[10] = {1,2,3,4,5,6,7,8,9,0}; int *p = arr;//p存放的是数组首元素的地址
- 既然可以把数组名当成地址存放到一个指针中,我们就可以使用指针来访问一个数组。
- 示例如下:
#include <stdio.h> int main() { int arr[] = {1,2,3,4,5,6,7,8,9,0}; int *p = arr; //指针存放数组首元素的地址 int sz = sizeof(arr)/sizeof(arr[0]); int i; for(i=0; i<sz; i++) { printf("&arr[%d] = %p <====> p+%d = %p\n", i, &arr[i], i, p+i); } return 0; }
- 所以 p+i 其实计算的是数组 arr 下标为i的地址。
那我们就可以直接通过指针来访问数组。 - 示例如下:
int main() { int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; int *p = arr; //指针存放数组首元素的地址 int sz = sizeof(arr) / sizeof(arr[0]); int i = 0; for (i = 0; i<sz; i++) { printf("%d ", *(p + i)); } return 0; }
四.二级指针
- 指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?
- 答案不言而喻—— 二级指针 。
int main() { int a = 10; int* p = &a; int** pa = &p; printf("%d\n", a); printf("%d\n", *p); printf("%d\n", **pa); return 0; }
- 我们可以看出来三者的值是相同的!!
- 对于二级指针的运算有:
//*pa通过对pa中的地址进行解引用,这样找到的是p ,*pa 其实访问的就是p . int b = 20; *pa = &b;//等价于 p = &b;
//**pa 先通过* pa 找到 p, 然后对 p 进行解引用操作:*p ,那找到的是 a . **pa = 30; //等价于*p = 30; //等价于a = 30;
五. 指针数组
- 指针数组是指针还是数组?
- 是数组。是存放指针的数组。
数组我们已经知道整形数组,字符数组。
int arr1[5]; char arr2[6];
- 那指针数组是怎样的?
int* arr3[5];//是什么?
- 还有一种指针类型叫做数组指针,是指针,这两种情况我都会放在指针进阶篇中介绍。
总结
- 今天的内容到这里就全部结束了,我们今天把指针基础部分全部介绍完了,其中主要包括野指针的定义以及如何避免,指针运算的几种形式以及指针与数组的关系和二级指针,希望大家把博客中的代码都弄懂哦!
- 好了,如果你有任何疑问欢迎在评论区或者私信我提出,大家下次再见啦!
新人博主创作不易,如果感觉文章内容对你有所帮助的话不妨三连一下这个新人博主再走呗。你们的支持就是我更新的动力!!!
**(可莉请求你们三连支持一下博主!!!点击下方评论点赞收藏帮帮可莉吧)**