指针(二)

一.野指针

1.野指针的定义

野指针即只想位置不知道的指针(随机的,不正确的,没有明确限制的)

2.野指针的成因以及规避方法

(1).指针未初始化

int main()
{
	int* p;//局部变量p没有初始化,默认为随机值
	*p = 20;
	return 0;
}

规避方法很简单,我们初始化就可以了;不知道初始化为什么,我们初始化为空(NULL)即可。

int main()
{
	int n = 10;
	int* p = &n;
	int* pp = NULL;
	return 0;
}

(2).指针越界访问

这个和数组越界访问差不多,越界了之后就不知道指向谁了。

int main()
{
	int arr[10] = { 0 };
	int* p = &arr[0];
	int i = 0;
	for (i = 0; i <= 11; i++)
	{
		*(p + i) = i;
	}
	return 0;
}

这个的预防措施也很简单,我们小心点不越界即可。

(3).指针指向的空间释放

#include<stdio.h>

int* test()
{
	int n = 1;
	return &n;
}

int main()
{
	int* p = test();
	printf("%d", *p);
	return 0;
}

我们要养成一个指针使用完毕之后置空的好习惯,还要养成一个判断指针是否为空的好习惯,下面我们来写一段代码。

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = &arr[0];
	for (int i = 0; i <= 10; i++)
	{
		*(p + i) = i;
	}
	//此时p已经越界了,所以我们要置空,以免下次使用出现bug
	p = NULL;
   //下次使用时,先判断是否为空,然后赋值。
	if (p == NULL)
	{
		p = &arr[10];
	}
	return 0;
}

2.assert断言

在C语言库中,assert.h头文件定义了宏assert(),用于在运行时确保程序可以符合指定条件,如果不符合就终止运行,并将报错信息打印到屏幕上。这个宏被称为“断言”。

assert(p!=NULL);

上述代码的意思是,如果p的确不等于空,那么程序就可以继续执行;如果p等于空,则则会报出错误信息。即括号内的条件如果为真则继续执行,如果为假则报错。我们现在可以修改一下1.(3)中的判断代码来使用它。

可以在截图的右上角看到,问题出现在第92行,即我们的assert断言处。

二.指针的使用和传址调用

1.strlen的模拟实现

库函数strlen的功能是求字符串长度,统计的是字符串中\0之前字符的个数。

实现思路是,参数接受一个字符串的起始地址,然后开始遍历整个字符串,直到遇到、0/

参考代码如下:

int my_strlen(char* str)
{
	int count = 0;
	assert(str);//当str为NULL时,判断为假。则报错。
	while (*str)//  '\0'的ASCII码代表0,即假。
	{           //因此当遇到\0时,循环结束。
		count++;
		*str++;
	}
	return count;
}

int main()
{
	char a[] = "abcdef";
	int len = my_strlen(&a[0]);
	printf("%d", len);
	return 0;
}

2.传值调用和传址调用

我们学习指针是为了解决问题的,那么什么问题是非指针不可的呢?

在没用学习指针前,我们可以是这样写代码的

例如:写⼀个函数,交换两个整型变量的值
⼀番思考后,我们可能写出这样的代码:

#include <stdio.h>
void Swap1(int x, int y)
{
int tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a=%d b=%d\n", a, b);
Swap1(a, b);
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}

但是我们运行之后,却发现并没有产生交换的效果。这是为什么呢?

我们先调试一下

我们发现在main函数内部,创建了a和b,a的地址是0x00cffdd0,b的地址是0x00cffdc4,在调⽤
Swap1函数时,将a和b传递给了Swap1函数,在Swap1函数内部创建了形参x和y接收a和b的值,但是
x的地址是0x00cffcec,y的地址是0x00cffcf0,x和y确实接收到了a和b的值,不过x的地址和a的地址不⼀样,y的地址和b的地址不⼀样,相当于x和y是独⽴的空间,那么在Swap1函数内部交换x和y的值,⾃然不会影响a和b,当Swap1函数调⽤结束后回到main函数,a和b的没法交换。Swap1函数在使⽤的时候,是把变量本⾝直接传递给了函数,这种调⽤函数的⽅式我们之前在函数的时候就知道了,这种叫传值调⽤

结论:实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实
参。

那么我们应该怎么办呢?当然是我们不传数值了,我们传地址,我们让这个函数去操纵地址不就完了吗!

void swap(int* a,int* b)
{
	int tmp = 0;
	tmp = *a;
	*a = *b;
	*b = tmp;
}
int main()
{
	int a = 10;
	int b = 100;
	swap(&a, &b);
	printf("a=%d\nb=%d", a, b);
	return 0;
}

我们使用这个函数即可完成任务,这个函数是通过传递地址而间接修改数据的。这种函数调用方式叫:传址调用

传址调用,可以让函数和主函数之间建立起真正的联系,在函数内部就可以修改主函数中的数值。

  • 21
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值