今天,我们继续来讲指针的内容
如果我们给定一个整型变量,那么这个变量的值是可以被修改的。为了使它的值不能被修改,那我们就可以使用const来修饰这个变量,如图:
#include <stdio.h>
int main()
{
int m = 0;
m = 20;//m是可以修改的
const int n = 0;
n = 20;//n是不能被修改的
return 0;
}上述代码中,我们给n前面加上const之后,它的值就不能被修改了。其实n本质是变量,只不过被const修饰后,在语法上加了限制,只要我们在代码中对n就⾏修改,就不符合语法规则,就报错,致使没法直接修改n。
但是,但是如果我们绕过n,使⽤n的地址,去修改n就能做到了,虽然这样做是在打破语法规则:
#include <stdio.h>
int main()
{
const int n = 0;
1
2
3
4
printf("n = %d\n", n);
int* p = &n;
*p = 20;
printf("n = %d\n", n);
return 0;
}如图中代码,不能直接修改n,那么就利用指针获取它的地址,再把地址里的值改掉,间接地改变n的值。
既然可以通过地址修改n,那我们也可以利用const使指针p得到n的地址,但是不能修改n的值.
那如何做到呢?也是利用const修饰它.⼀般来讲const修饰指针变量,可以放在*的左边,也可以放在*的右边,意义是不⼀样的。
int * p; //没有const修饰
int const * p;//const 放在*的左边做修饰,或者 const int* p;
这种情况下,const修饰的是p所指向的内容,此时const能保证该内容不会被修改;
int * const p;//const 放在*的右边做修饰
这种情况下,const修饰指针变量本身,此时const能保证指针的指向不会被改变;
const int* const p; //const在*两边都存在
这种情况下,const修饰指针变量和指针指向的内容,即两个都不能改变;
需要注意 :单个const时,总是只能锁定一个,另一个可以正常被修改!
概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}如图,局部指针变量未初始化,默认是随机值;
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = &arr[0];
int i = 0;
for(i = 0; i <= 11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}当指针指向的范围超出数组arr的范围时,p就是野指针;
#include <stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("%d\n", *p);
return 0;
}在主函数中,上述代码想实现利用*p调用函数,再打印出该函数的返回值&n,问题是test函数运行完后,n的生命周期结束,空间被释放,导致printf中解引用p时,访问的是一块已经被释放,不再有效的内存区域,这导致p成为了野指针;
只要是NULL指针就不去访问
1.指针的初始化
如果明确知道指针该指向哪里,那就直接赋地址,若不知道,则赋值NULL,初始化如图:
int num = 10;
int*p1 = #
int*p2 = NULL;//赋值NULL一个程序中申请了哪些内存空间,那就只能指向哪些空间,不能访问没有申请的空间;
当指针变量指向⼀块区域的时候,我们可以通过指针访问该区域,后期不再使⽤这个指针访问空间的时候,我们可以把该指针置为NULL,同时,使用一个指针时可以检查它是否为NULL,是的话不可以直接使用,不是的话可以使用
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
int i = 0;
for(i = 0; i < 10; i++)
{
*(p++) = i;
}
//此时p已经越界了,可以把p置为NULL
p = NULL;
//下次使⽤的时候,判断p不为NULL的时候再使⽤
//...
p = &arr[0];//重新让p获得地址
if(p != NULL) //判断
{
//...
}
return 0;
}例子如野指针成因3中描述,尽量避免返回局部变量的地址,返回也需要在其生命周期内;
assert.h 头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报
错终⽌运⾏。这个宏常常被称为“断⾔”。
assert(p != NULL);
这句的作用是检测p是否为NULL,当程序走到这一步时,会判断,若NULL,则会终止运行;
assert() 宏接受⼀个表达式作为参数。如果该表达式为真(返回值⾮零), assert() 不会产⽣
任何作⽤,程序继续运⾏。如果该表达式为假(返回值为零), assert() 就会报错
果已经确认程序没有问题,不需要再做断⾔,就在 #include <assert.h> 语句的前⾯,定义⼀个宏 NDEBUG
#define NDEBUG
#include <assert.h>进行此操作后,重新编译程序,编译器就会禁⽤⽂件中所有的 assert() 语句,这样省掉了一条条断言注释掉的功夫,反之一样。
assert() 的缺点:引⼊了额外的检查,增加了程序的运⾏时间
还有,这玩意程序员们经常使用,用来检查bug,在VS集成开发环境中,我们可以在debug中使用(为了排查bug),而在release版本中,这功能被优化掉了(为了效率)。
ok,今天的内容就讲到这里,期待我的下一章指针内容吧,请各位还不忘点个小小的赞!