指针(3)

 

目录

 

1.const修饰变量

 2.指针运算

• 指针+- 整数

• 指针-指针

 模拟实现strlen  

• 指针的关系运算

 3.野指针

4. 如何规避野指针

5. assert断言

6.指针的使⽤和传址调⽤

7 传值调⽤和传址调⽤


1.const修饰变量

变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。 但是如果我们希望⼀个变量加上⼀些限制,不能被修改,怎么做呢?这就是const的作⽤

#include <stdio.h>
int main()
{
	 const int n = 100;//const叫做常变量,被修饰的变量本质还是变量,
	 //但是不能被修改的变量dd
	n = 200;
	printf("%d", n); 
	return 0;
}

 想要限制*p可以在* 左边加const 

#include <stdio.h>
int  main()
{
	int a = 10;
	int const * p = &a;//如果在*号左边加const 就会限制* p ,
	*p = 200;//这里的* p出现错误了
	p = 300;
	return 0;
}

当你运行时编译器会发生错误 

 

 当你把const放在* 的 右边会怎么样?

 当然运行结果和上面也是一样的错误,有同学就会说: 那如果把两变都加const会怎么样呢?

 可以跟据上面的结果推算,两个都不能使用, 如图所示

 

 2.指针运算

指针的基本运算有三种,分别是

• 指针+- 整数

 用指针加减整数 来打印数组 

#include <stdio.h>
int main()//用指针的方式来打印数组
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	int* p = &arr[0];
		for (i = 0; i < sz; i++)
	{
		printf("%d ", *p);
		p++;//指针加整数的关系
	}

	return 0;

当然也可以这样写


#include<stdio.h>
int main()//用指针的方式来打印数组
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	int* p = &arr[0];//让p指向arr[0]的地址
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *( p+i ));//这里的p是存的地址  i在循环增加,*取出数组下标的内容	
	}
	return 0;
}

• 指针-指针

 指针-指针的绝对值是计算指针间的元素个数,前提是两个指针都是同一个空间的。

可以猜一猜下面的代码运行结果是什么?

#include <stdio.h>
int main()
{

	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf(" %d", &arr[9] - &arr[0]);//指针减去指针求的是同一个空间指针的元素个数
	return 0;
}

结果是元素之间的个数

 

 模拟实现strlen  

#include <stdio.h>
#include  <string.h>//如果使用strlen 要包含的头文件
size_t My_strlen(char* p)//用char类型指针接收
{
	int count = 0;
	while (*p != '\0')
	{
		count++;//如果不是‘\0’就+1
		p++;//p也向后加
	}
	return count;//次数为几就返回几
 }

int main()
{
	char* p = "abcde";
	 size_t len = My_strlen(p);//创建一个自己的strlen 
	 printf("%zd ", len);
	return 0;

指针减指针 也可以实现

#include <stdio.h>
size_t My_strlen(char* p)
{
	char* start = p;//给一个变量start 成为首地址,
	char* end = p;//再给一个变量end  让他向后找。
	while (*end != '\0')//当end找到\0 就会停止
	{
		end++;
	}
	return end - start;// 返回end- start 的元素个数

 }
int main()
{

	char* arr = "asdefg";
	size_t len = My_strlen(arr);
		printf("%zd ", len);

	return 0;

}

 

• 指针的关系运算

 

#include<stdio.h> 
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = arr;//数组名就是首元素的地址
	
	while (p < &arr[sz])//这里是地址的大小比较
	{
		printf("%d ", *p);
		p++;

	}
	return 0;
}

 3.野指针

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

#include <stdio.h>
int main()
{
	int arr[10] ={ 0 };
	int* p = arr;
	for (int i = 0; i <= 11; i++)//这里的越界访问会造成野指针
	{
		printf("%d ", *p);
		p++;
	}
	return 0; 
}

4. 如何规避野指针

     如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL. NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址 会报错

#include <stdio.hj>
int main()
{
	int arr= NULL;
	int* p = &arr;
	if (*p != NULL)//可以判断一下p的内容是不是空指针
	{
		*p = 100;
	}
	else
	{
		printf("是空指针\n");
	}
	return 0;
}

5. assert断言

assert.h 头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报 错终⽌运⾏。这个宏常常被称为“断⾔”

正如上述代码也可以改成下面代码

#include<stdio.h>
#include <assert.h>//包含的assert的头文件
int main()

{
	int arr = 100;//当arr等于100的时候程序什么没问题
	assert(arr != NULL);
	{
	
	}
	return 0;
}

但是当arr等于NULL的时候程序会怎么样?

 这里程序会直接崩溃

如果assert 不想使用了,那么先别着急注释掉,下面有一个好玩的东西可以像开关一样控制它。 那么就是在<assert .h>定义的头文件前定义一个 #define NDEBUG 这样的一条语句。

#include <stdio.h>
#define NDEBUG//  这个时候的assert已经被关闭了,
//如果把这条语句注释掉,assert就可以正常使用了
#include <assert.h>//包含的assert的头文件
int main()
{
	int arr = NULL;
	assert(arr != NULL);
	{
	
	}
	return 0;
}

 

⼀般我们可以在 Debug 中使⽤,在 Release 版本中选择禁⽤ assert 就⾏,在 VS 这样的集成开 发环境中,在 Release 版本中,直接就是优化掉了。这样在debug版本写有利于程序员排查问题, 在 Release 版本不影响⽤⼾使⽤时程序的效率。

6.指针的使⽤和传址调⽤

strlen的模拟实现 库函数strlen的功能是求字符串⻓度,统计的是字符串中 \0 之前的字符的个数

可以用assert来判断表达式,如果不想arr数组被修改 可以再变量左边加上const,配合const使用   

#include <stdio.h> 

#include <assert.h>
size_t My_strlen(char const *p)//如果期望arr字符串不被修改 可以加上const
{
    int count = 0;

    assert(*p != '\0');
    while (*p)
    {
            count++;
            p++;
    }
    return count;
 }

int main()
{
    char arr[] = "adkfjd";

    size_t len = My_strlen(arr);
    printf("% zd ", len);
    return 0;

}

7 传值调⽤和传址调⽤

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

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

然而你发现打印来的结果并不是你想要的结果。

 这是哪里出了问题呢? 

下面可以的出,形参的改变不会影响实参 。这里交换的是x 和y 

 

 想要改变a和b 的数值,可以在函数传递时 传地址上去 代码如下:

#include <stdio.h>
void  Swap(int *pa, int* pb)//如果传的是地址 那么得用指针接收
{
	int t = 0;
	t = *pa;//t =a;
	*pa = *pb;//a=b;
	*pb = t;//b=t;
//这样就完成的交换	
 }
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d%d", &a, &b);
	printf(" 交换前%d %d\n", a, b);
	Swap(&a, &b);
	 printf(" 交换后%d %d\n", a, b);
	return 0;
}

 那么就大功告成了

 结束了下班 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值