指针核心知识:5篇系统梳理2

本系列将通过 5 篇文章系统梳理指针核心知识,欢迎关注专栏指针梳理,获取完整内容


1.const修饰指针

const修饰指针变量,可以放在*的左边,也可以放在*的右边,意义是不一样的

cpp 复制代码
int* p;//没有const修饰
int const* p;//const 放在*的左边做修饰 
int* const p;//const 放在*的右边做修饰 

你们可以打开自己的编译器编译一下代码,看是否有问题

cpp 复制代码
#include <stdio.h>
//代码1 - 测试无const修饰的情况 
void test1()
{
	int n = 10;
	int m = 20;
	int* p = &n;
	*p = 20;//ok?
	p = &m; //ok?
}
//代码2 - 测试const放在*的左边情况 
void test2()
{
	int n = 10;
	int m = 20;
	const int* p = &n;
	*p = 20;//ok?
	p = &m; //ok?
}
//代码3 - 测试const放在*的右边情况 
void test3()
{
	int n = 10;
	int m = 20;
	int* const p = &n;
	*p = 20; //ok?
	p = &m; //ok?
}
//代码4 - 测试*的左右两边都有const 
void test4()
{
	int n = 10;
	int m = 20;
	int const* const p = &n;
	*p = 20; //ok?
	p = &m; //ok?
}
int main()
{
	//测试无const修饰的情况 
	test1();
	//测试const放在*的左边情况 
	test2();
	//测试const放在*的右边情况 
	test3();
	//测试*的左右两边都有const 
	test4();
	return 0;
}

结论:const在修饰指针变量的时候

const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。 但是指针变量本身的内容可变。

const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。

2.野指针

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

野指针成因

1.指针未初始化

cpp 复制代码
#include <stdio.h>
int main()
{
	int* p;//局部变量指针未初始化,默认为随机值 
	*p = 20;
	return 0;
}

局部变量指针未初始化,默认为随机值

2.指针越界访问

cpp 复制代码
#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就是野指针

3.指针指向的空间释放

cpp 复制代码
#include <stdio.h>
int* test()
{
	int n = 100;
	return &n;
}

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

3.如何规避野指针

指针初始化

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

代码举例:

cpp 复制代码
#include <stdio.h>
int main()
{
	int num = 10;
	int* p1 = &num;
	int* p2 = NULL;

	return 0;
}

小心指针越界

一个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是越界访问。

指针变量不再使用时,及时置NULL,指针使用之前检查有效性

当指针变量指向一块区域的时候,我们可以通过指针访问该区域,后期不再使用这个指针访问空间的时候,我们可以把该指针置为NULL。因为约定俗成的一个规则就是:只要是NULL指针就不去访问, 同时使用指针之前可以判断指针是否为NULL。

代码举例:

cpp 复制代码
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个例子,不要返回局部变量的地址

4.assert 断言

assert.h 头文件定义了宏 assert() ,用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行。这个宏常常被称为**"断言"**

cpp 复制代码
assert(p != NULL);

assert() 宏接受一个表达式作为参数。如果该表达式为真(返回值非零), assert() 不会产生任何作用,程序继续运行。如果该表达式为假(返回值为零), assert() 就会报错,在标准错误流 stderr 中写入一条错误信息,显示没有通过的表达式,以及包含这个表达式的文件名和行号。如果已经确认程序没有问题,不需要再做断言,就在 #include 语句的前面,定义一个宏 NDEBUG 。

cpp 复制代码
#define NDEBUG
#include <assert.h>

重新编译程序,编译器就会禁用文件中所有的 assert() 语句。如果程序又出现问题,可以移除这条 #define NDEBUG 指令(或者把它注释掉),再次编译,这样就重新启用了 assert() 语句。 assert() 的缺点是,因为引入了额外的检查,增加了程序的运行时间。

5.传值调用和传址调用

传值调用

写一个函数,交换两个整型变量的值

cpp 复制代码
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;
}

结果:

我们发现其实没产生交换的效果

我们调试时发现,传值调用只是把值传递了过去,实参和形参本质上是独立的空间,对形参的修改不会影响实参,这种叫传值调用

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

传址调用

cpp 复制代码
#include <stdio.h>
void Swap2(int* px, int* py)
{
	int tmp = 0;
	tmp = *px;
	*px = *py;
	*py = tmp;
}
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	printf("交换前:a=%d b=%d\n", a, b);
	Swap2(&a, &b);
	printf("交换后:a=%d b=%d\n", a, b);
	return 0;
}

我们发现这种方式就完成了两个数的交换

这里调用Swap2函数的时候是将变量的地址传递给了函数,这种函数调用方式叫:传址调用

传址调用,可以让函数和主调函数之间建立真正的联系,在函数内部可以修改主调函数中的变量;所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采用传值调用。如果函数内部要修改主调函数中的变量的值,就需要传址调用。

本系列其余文章,可关注专栏查看,持续更新中

相关推荐
WangJunXiang62 小时前
Nginx性能优化与监控笔记
笔记·nginx·性能优化
四谎真好看2 小时前
Redis学习笔记(实战篇2)
redis·笔记·学习·学习笔记
wjm0410062 小时前
ios学习路线-- swift基础2
学习·ios·swift
科技林总2 小时前
【系统分析师】第12章 软件架构设计
学习
北岛寒沫2 小时前
北京大学国家发展研究员 中国经济专题 课程笔记(第二课 农村土地改革)
经验分享·笔记·学习
西装没钱买2 小时前
C语言组播的使用
c语言·开发语言·udp·组播·组播绑定网卡
Piccab0o2 小时前
【学习笔记】——电磁相关
笔记·学习
阿Y加油吧3 小时前
力扣打卡——盛最多水的容器、三数之和
算法·leetcode·排序算法
boy快快长大3 小时前
【PyTorch】2.0 入门学习
人工智能·pytorch·学习