目录
2.2.3指针变量不再使用时,及时置为NULL,指针使用之前检查其有效性
1指针运算
1.1指针加减整数
因为在数组中是连续存放的,只要知道第一个元素的地址,顺藤摸瓜就知道所有的元素。可以通过指针来实现数组的访问。代码如下:
这就是通过指针的方式来打印数组。
1.2指针的关系运算
指针其实是能比较大小的,前面了解到数组的地址是连续的,因此,我们可以通过利用指针的大小来进行访问数组。代码展示:
这是指针比较大小的一种使用方法。
2野指针
野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)
2.1野指针成因
2.1.1指针未初始化
int main(){
int* p;//局部变量指针未初始化,默认为随机值
*p = 1;
return 0;
}
2.1.2指针越界访问
int main(){
int data[] = { 0,1,2,3,4,5,6,7,8,9, };
int ze = sizeof(data) / sizeof(data[0]);
int* p = data;
for (int i = 0; i <11; i++) {
printf("%d", *(p+i));
}
return 0;
}
数组只有1位,这里打印了11位,超出数组范围,这里p就是野指针。
2.1.3指针指向的空间已释放
int* test() {
int n = 100;
return &n;
}
int main(){
int* p = test();
printf("%d\n", *p);
return 0;
}
变量n是函数里创建的,出了函数创建的变量就会释放,指针再访问已释放的空间就是野指针。
2.2如何避免野指针
2.2.1指针初始化
如果明确知道指针指向哪里就直接赋值地址,如果不知道指针应该指向哪里,可以给指针赋值NULL,NULL是C语言中定义的一个标识符常量,值是0,0也是地址,这个地址是无法使用的,读写该地址会报错 。
初始化如下:
int main(){
int a = 0;
int* p1 = &a;
int* p2 = NULL;
return 0;
}
2.2.2小心指针越界
一个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是越界访问。
2.2.3指针变量不再使用时,及时置为NULL,指针使用之前检查其有效性
2.2.4避免返回局部变量的地址
如造成野指针的第三个例子,不要返回局部变量的指针。
3assert断言
assert.h头文件定义了宏assert(),用于运行时确保程序符合指定条件,如果不符合,就会报错终止运行,这个宏常常被称为断言。
assert()使用时,括号里的条件如果为真就会就会继续执行,如果为假,则会报错程序停止
assert()的使用对程序员非常友好,好处如下:它不仅能自动标识文件和出问题的行号,还有一种无需更改代码就能开启或关闭assert()的机制。如果已经确认程序没有问题,不需要再做断言,就在#include<assert.h>语句前面,定义一个宏#define NDEBUG。
然后重新编译代码,编译器就会禁用文件中所有的assert()语句,如果程序又出现问题,可以将#define NDEBUG移除或者注释掉,就会重新启用assert()语句。
assert的缺点是引入额外的检查,增加程序运行的时间。
一般我们在Debug中使用,在reverse版本中选择禁用assert就行,在VS这样的集成开发环境中,reverse版本会直接优化掉assert,这样在Debug版本有利于程序员排查问题,在Reverse版本中不影响用户使用时程序的效率 。