指针运算
指针基本运算有三种,分别是:
- 指针加减整数
- 指针减指针
- 指针的关系运算
指针±整数
因为数组是连续存放的,所以只要知道了首元素的地址就可以顺藤摸瓜,找到其他元素。
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
int* p = &arr[0];
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
这里我们可以发现,通过指针加减一个整数可以实现指针的左右移动,而得到其他元素。
我们要注意代码中的sz的写法,是得到数组元素的常用的方法。因为我们可能有时候不知道数组大小,可以这样算出来。
指针减指针
指针减指针前提是两个指针指向同一块空间,得到的是两指针间的元素个数(绝对值)。
int main()
{
int arr[10] = { 0 };
printf("%d\n", &arr[9] - &arr[0]);//指针-指针
printf("%d\n", &arr[0] - &arr[9]);//指针-指针
//指针-指针的绝对值是指针和指针之间的元素个数
char ch[6] = {0};
printf("%d\n", &arr[9] - &ch[4]);//err
return 0;
}
指针的关系运算
指针也是可以比较大小的,这就是指针的关系运算
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = &arr[0];
while (p < arr + sz)//指针的关系元素
{
printf("%d ", *p);
p++;
}
return 0;
}
野指针
概念:野指针就是指向位置不可知的指针(随机的,不正确的,没有限制的)
野指针成因
- 指针未初始化
int main()
{
int* p;//未初始化,指向不明
*p = 20;
printf("%d\n", *p);
return 0;
}
- 指针访问越界
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;
}
- 指针指向空间释放
int* test()
{
int n = 100;
return &n;
}
int main()
{
int* p = test();
printf("%d\n", *p);
return 0;
}
这里,n为局部变量,当test函数结束后n被销毁,空间回收,指针会成为野指针。
规避野指针
- 指针初始化:当明确知道指向位置时,直接赋值,不知道是赋值NULL。NULL值为0,这也是地值,但不可用,读写该地址会报错。
- 小心越界:当我们申请空间时,指针不能超出这个范围。
- 指针不用时,及时赋值NULL,使用前检查有效性。
- 避免返回局部变量地址。
assert断言
assert() 宏用法
注意:assert是宏,而不是函数。在C的assert.h头文件中。
assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行,
assert() 的用法很简单,我们只要传入一个表达式,它会计算这个表达式的结果:如果表达式的结果为“假”,assert() 会打印出断言失败的信息,并调用 abort() 函数终止程序的执行;如果表达式的结果为“真”,assert() 就什么也不做,程序继续往后执行。
int main()
{
int a = 10;
int* p = NULL;
assert(p != NULL);
//if (p == NULL)
//{
//printf("p == NULL, 是无效的指针\n");
//return 1;
//}
*p = 20;
printf("%d\n", a);
return 0;
}
assert的作用就是注释的判断的作用。
当我们在使用一个指针时,可先用assert判断其是否可用。
今天,就先分享到这里,我们下期再见。