前言: 书接上回,由于文章过长,所以分了几篇文 若内容对大家有所帮助,可以收藏慢慢看,感谢大家支持 谢谢大家 ! ! !
就像流浪猫、流浪狗一样没有家一样 野指针就是指针指向的位置是未知的
那么为什么会出现野指针呢? 主要有以下几点:
int * p ;
* p = 20 ;此处的p指针未初始化,就是一个野指针,其所指向的地址为随机值 而之后的 * p 就会非法访问
通过指针遍历数组时超出数组范围 就像未经许可闯入他人房间属于非法访问
int arr[10] = {0};
int* p = %arr[0];
p = p + 10;
*p = 100;
//此处的 p 就属于野指针 当 p 指向数组有效范围(如arr[0 ] ~ arr [ 9 ] )时是合法指针一旦 p 指向 arr [ 10 ] 及之后的位置,就变成野指针 以上末尾的 p 就属于野指针,无法访问 arr[ 11 ] ,越界访问
#include <stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int* p = test();
printf("%p\n\n", p);
return 0;
}当出了 test ( ) 函数时,n 的内存空间被释放 虽然指针变量本身仍存在,但指向的空间已无效 继续通过该指针访问内存可能导致程序崩溃或数据错误 故 p 为野指针,是非法访问
必须养成指针初始化的习惯,未初始化的指针不知道指向何处 若明确知道指针应指向的地址,直接赋地址值; 若不确定指针指向,应赋值为NULL(空指针) (NULL是C语言中的标识符常量,其值为0)
例如:
int * p = NULL;//此地址无法使用不超出范围访问
int * p = NULL;//指针不使用时,置 NULL
。。。。。。。
。。。。。。。
。。。。。。。
if( p != NULL)//使用前检查有效性
*p = 100;当确认不是NULL时再使用该指针
在编写程序过程中,需要一个提醒代码漏洞/Bug的工具,以便于程序员及时修改和完善代码 assert关键字就是这个工具
assert 是定义在 < assert.h > 头文件中的一个宏 用于在运行时确保程序符合指定条件 若不符合条件,就报错 该宏被称为断言。
assert(p != NULL);若 p 为空指针,就报错;若不为空,则运行
另外 使用前记得包含头文件<assert.h> 在头文件前面可定义开关来控制 assert # include DEBUG为开 # include NDBUG为关 可随意控制开关
代码演示:(不报错)
#define DEBUG
#include<stdio.h>
#include<assert.h>
int main()
{
int g = 100;
int* p = &g;
assert(p != NULL);
*p = 1000;
printf("%d\n\n", g);
return 0;
}运行结果:(不报错)

代码演示:(报错)
#define DEBUG//定义控制assert打开
#include<stdio.h>
#include<assert.h>
int main()
{
int g = 100;
int* p = &g;
p = NULL;
assert(p != NULL);
*p = 1000;
printf("%d\n\n", g);
return 0;
}运行结果:(报错)

我们可以看到打印了如下信息: Assertion failed: p != NULL, file D:\学校\C语言\QwQ\QwQ\QwQ.c, line 11 (这就是错误的具体位置)
这就是assert的好处了: 可以自动标识文件和出问题的行号
代码演示:(关闭assert)
#define NDEBUG//定义控制assert关闭
#include<stdio.h>
#include<assert.h>
int main()
{
int g = 100;
int* p = &g;
p = NULL;
assert(p != NULL);
*p = 1000;
printf("%d\n\n", g);
return 0;
}运行结果:

这里用 #define NDEBUG 定义控制assert关闭
可以看到,代码正常运行,未出现报错, 如果不仔细发现,是找不到错误的,这就是 asser t的好处了
综上所述,assert的好处还是很多的: 1.可以自动标识文件和出问题的行号 2.可以通过定义来随意控制开关 assert( ) 但是要注意: assert 引入了检查,一定程度上增加了运行时间,故确定指针无问题后,可禁用 assert 来减少运行时间 另外,VS 中 Release 版本中默认优化了 assert
变量 n 被 const 修饰后,致使无法直接修改 n ,修改就会报错
代码演示:
#include <stdio.h>
int main()
{
const int n = 10;//const 修饰变量
n = 100;
printf("%d\n\n", n);
return 0;
}运行结果:(报错)


这里可见: const 阻止了对变量 n 的修改,若要强行直接修改就会报错停止运行
但是,不能直接对值进行修改,能通过地址对 变量进行修改 代码演示:
#include <stdio.h>
int main()
{
const int n = 10;//const修饰变量
int* p = &n;//取出n的地址
*p = 100;//通过指针对n进行修改
printf("%d\n\n", n);
return 0;
}运行结果:

这里可以看到,n 的值确实被指针间接修改了 所以const 修饰变量后不能直接对值进行修改,但能通过地址对变量进行修改
const 修饰指针变量有两种形式 1.
int * p ;(无修饰) 2.int const * p ;或const int * p ;(const放在 * 左边修饰) 3.int * const p ;(const放在 * 右边修饰)
int const * p ;
const int * p ;
//二者等价限制的是指针指向的内容( 即 * p ) 不能通过指针变量来修改它所指向的内容 但指针变量本身的内容( 存储的地址 )可以改变
代码演示:(改变指针所指向的内容)(报错)
#include <stdio.h>
int main()
{
int n = 10;
int const* p = &n;
//取出n的地址
//const修饰指针变量
*p = 100;//通过指针对n进行修改
printf("%d\n\n", n);
return 0;
}运行结果:(改变指针所指向的内容)(报错)


代码演示:(改变指针本身内容)(不报错)
#include <stdio.h>
int main()
{
int n = 10;
int m = 100;
int const* p = &n;
//取出n的地址
//const修饰指针变量
p = &m;//改变 p 指针的内容
printf("%d\n\n", *p);
return 0;
}运行结果:(改变指针本身内容)(不报错)

由以上两段代码可知:
const放在 * 左边修饰指针变量: 限制的是指针指向的内容( 即 * p ) 不能通过指针变量来修改它所指向的内容 但指针变量本身的内容( 存储的地址 )可以改变
int * const p ;与const放在 * 左边修饰相反 限制的是指针变量本身的内容 不能修改指针变量本身的内容( 存储的地址 ) 但指针指向的内容( 即 * p )可以改变
代码演示:(改变指针本身内容)(报错)
#include <stdio.h>
int main()
{
int n = 10;
int m = 100;
int* const p = &n;
//取出n的地址
//const修饰指针本身内容
p = &m;//改变 p 指针的内容
printf("%d\n\n", *p);
return 0;
}运行结果:(改变指针本身内容)(报错)


代码演示:(改变指针所指向的内容)(不报错)
#include <stdio.h>
int main()
{
int n = 10;
int* const p = &n;
//取出n的地址
//const修饰指针本身内容
*p = 100;
printf("%d\n\n", n);
return 0;
}运行结果:(改变指针所指向的内容)(不报错)

由以上两段代码可知:
const放在 * 右边修饰变量: 限制的是指针变量本身的内容 不能修改指针变量本身的内容( 存储的地址 ) 但指针指向的内容( 即 * p )可以改变