一野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
补充
局部变量如果不初始化变量的值是随机地
全局变量,如果不初始化变量的值默认0
静态变量和全局变量一样
野指针成因
指针未初始化,指针越界访问,指针指向的空间释放
下面是错误的
#include <stdio.h>
int main()
{
int* p;
printf("%p\n", p);
}
正确的
#include <stdio.h>
int main()
{
int* p;
p = 10;
printf("%p\n", p);
}
指针越界访问
#include <stdio.h>
int main()
{
int arr[5] = {0};
int i = 0;
int* pa = arr;
for (i = 0; i < 10; i++)
{
*pa = 1;
pa++;
}
return 0;
}
指针指向的空间释放
#include <stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("%d\n", *p);
return 0;
}
#include <stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("hehe\n")
printf("%d\n", *p);
return 0;
}
在上面的代码,当我们进入我们自己自定义函数的时候再出来,我们的空间被释放。
1,如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL.
int *pa=NULL //就这样
NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址 会报错。
上面什么对没
2,⼩⼼指针越界
⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是 越界访问。
指针变量不再使⽤时,及时置NULL,指针使⽤之前检查有效性
当指针变量指向⼀块区域的时候,我们可以通过指针访问该区域,后期不再使⽤这个指针访问空间的 时候,我们可以把该指针置为NUL因为约定俗成的⼀个规则就是:只要是NULL指针就不去访问, 同时使⽤指针之前可以判断指针是否为NULL。
二,assert断⾔
assert.h 头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报 错终⽌运⾏。这个宏常常被称为“断⾔”。
1,介绍断言
编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设,可以将断言看作是异常处理的一种高级形式。断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真。可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言,而在部署时禁用断言。同样,程序投入运行后,最终用户在遇到问题时可以重新启用断言。
使用断言可以创建更稳定,品质更好且不易于出错的代码。当需要在一个值为FALSE时中断当前操作的话,可以使用断言。单元测试必须使用断言(Junit/JunitX)。
除了类型检查和单元测试外,断言还提供了一种确定各种特性是否在程序中得到维护的极好的方法。
使用断言使我们向按契约式设计更近了一步。
2,断言可以有两种形式
1.assert Expression1
2.assert Expression1:Expression2
其中Expression1应该总是一个布尔值,Expression2是断言失败时输出的失败消息的字符串。如果Expression1为假,则抛出一个 AssertionError,这是一个错误,而不是一个异常,也就是说是一个不可控制异常(unchecked Exception),AssertionError由于是错误,所以可以不捕获,但不推荐这样做,因为那样会使你的系统进入不稳定状态。
3assert() 语句禁用
#define NDEBUG
#include <assert.h>
然后,重新编译程序,编译器就会禁⽤⽂件中所有的 assert() 语句。如果程序⼜出现问题,可以移
除这条 #define NDBUG 指令(或者把它注释掉),再次编译,这样就重新启⽤了 assert() 语 句。
4,实践assert
我们看下面的代码
int main()
{
int a = 10;
int* p = &a;
p= NULL;
assert(p != NULL);
return 0;
}
系统会对上面的代码进行报错;assert() 宏接受⼀个表达式作为参数。如果该表达式为真(返回值⾮零), assert() 不会产⽣
任何作⽤,程序继续运⾏。如果该表达式为假(返回值为零), assert() 就会报错,在标准错误 流 stderr 中写⼊⼀条错误信息,显⽰没有通过的表达式,以及包含这个表达式的⽂件名和⾏号。
但是assert不仅仅使用于指针例如
int main()
{
int a = 10;
int* p = &a;
p= NULL;
int b = 20;
assert(b != 5);
return 0;
}
5,assert的缺点
assert() 的缺点是,因为引⼊了额外的检查,增加了程序的运⾏时间。
⼀般我们可以在 Debug 中使⽤,在 Release 版本中选择禁⽤ assert 就⾏,在 VS 这样的集成开 发环境中,在 Release 版本中,直接就是优化掉了。这样在debug版本写有利于程序员排查问题, 在 Release 版本不影响⽤⼾使⽤时程序的效率
三,指针的使⽤和传址调⽤
首先 ;实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实
我们看下面的代码
#include <stdio.h>
void Swap1(int x, int y)
{
int tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 10;
int b = 30;
printf("交换前:a=%d b=%d\n", a, b);
Swap1(a, b);
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
我们看到上面的代码的是并没有进行交换,这是为什么呢,我们可以用vs调试进行,这里就不进行了,代码的联系一定要自己去实践
我们可以看正确的
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void add(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int main()
{
int a = 10;
int b = 30;
printf("交换前:a=%d b=%d\n", a, b);
add(&a, &b);
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
拜拜