前面的文章对于指针与指针变量、指针变量的定义、指针数组、结构体指针等指针内容进行了讲解,但是指针这部分知识点还是很庞大和细碎的,所以,这里再和大家分享一下指针中值得我们注意的重要知识点。
目录
1.野指针
概念: 野指针就是指针指向的位置是不可知的、随机的、不正确的、没有明确限制的,换句话说野指针就是其指向有可能很危险的指针。
这种指针如果不加分辨就对其进行操作,有可能引起系统的崩溃,严重地甚至会导致系统死机。
1.1野指针的成因
①指针未初始化
#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;
}
③指针指向的空间释放
#include <stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("%d\n", *p);
return 0;
}
上面代码的问题在于看似给指针赋初值了,但事实上函数test建立的变量n在函数运行结束后就自动销毁了,也就是&n指向的空间释放了,所以,实际上指针p指向的空间还是随机的。
1.2如何避免野指针
①指针初始化
如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪里,可以给指针赋值为NULL. NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使用的,读写该地址会报错。
#include <stdio.h>
int main()
{
int num = 10;
int*p1 = #
int*p2 = NULL;
return 0;
}
比较科学的初始化就是上面代码所呈现的两种。
②小心指针越界
⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是越界访问。(这种情况比较容易出现在数组的使用中,所以我们要多留意自己建立数组的大小)
③指针变量不再使用时,及时赋值NULL,指针使⽤之前检查有效性
当指针变量指向⼀块区域的时候,我们可以通过指针访问该区域,后期不再使⽤这个指针访问空间的时候,我们可以把该指针置为NULL。因为约定俗成的⼀个规则就是:只要是NULL指针就不去访问, 同时使⽤指针之前可以判断指针是否为NULL。
④避免返回局部变量的地址
避免③野指针的出现。
⑤注意对于assert的使用
assert.h 头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报错终止运行。这个宏常常被称为“断⾔”。
assert(p != NULL);
上⾯代码在程序运行到这⼀行语句时,验证变量 p 是否等于 NULL 。如果确实不等于 NULL ,程序继续运行,否则就会终止运行,并且给出报错信息提示。
assert() 宏接受⼀个表达式作为参数。如果该表达式为真(返回值非零), assert() 不会产生任何作用,程序继续运行。如果该表达式为假(返回值为零), assert() 就会报错,在标准错误流 stderr 中写⼊⼀条错误信息,显示没有通过的表达式,以及包含这个表达式的文件名和行号。
2.sizeof和strlen的对比
2.1 sizeof
在之前讲解运算符的时候,我们没有详细讲解sizeof ,在这里和大家讲解一下sizeof。 sizeof 计算变量所占内存内存空间大小的,单位是字节,如果运算对象是类型的话,计算的是使⽤类型创建的变量所占内存空间的大小。
2.2 strlen
strlen 是C语⾔库函数,功能是求字符串⻓度。函数原型如下:
size_t strlen ( const char * str );
统计的是从 strlen 函数的参数 str 中这个地址开始向后, \0 之前字符串中字符的个数。 strlen 函数会⼀直向后找 \0 字符,直到找到为止,所以可能存在越界查找。
2.3 sizeof 和 strlen的对比
sizeof:
① sizeof是操作符
② sizeof计算操作数所占内存的大小,单位是字节
③ 不关注内存中存放什么数据
strlen:
① strlen是库函数,使⽤需要包含头⽂件 string.h
②srtlen是求字符串⻓度的,统计的是‘\0’之前字符的隔个数
③关注内存中是否有‘\0’,如果没有‘\0’,就会继续往后找,可能会越界
下面是一些很雷同的代码希望,大家认真敲一敲,总结总结,从而加深对于sizeof和strlen的认识及理解,同时能更好地明白两者之间的不同之处。
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a+0));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(a[1]));
printf("%d\n",sizeof(&a));
printf("%d\n",sizeof(*&a));
printf("%d\n",sizeof(&a+1));
printf("%d\n",sizeof(&a[0]));
printf("%d\n",sizeof(&a[0]+1));
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
char arr[] = "abcdef";
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));
后面两大段代码建议建议对比进行哦。