首页 > 其他分享 >【落羽的落羽 C语言篇】指针·之其二

【落羽的落羽 C语言篇】指针·之其二

时间:2024-11-16 10:18:29浏览次数:3  
标签:const 变量 落羽 C语言 assert int 地址 指针

在这里插入图片描述

文章目录

  • 一、const 使用指南
    • 1. const修饰变量
    • 2. const修饰指针变量
  • 二、野指针
    • 1. 野指针的概念
    • 2. 野指针的成因
    • 3. 如何规避野指针
  • 三、assert(断言) 使用指南
  • 四、传值调用和传址调用
    • 1. 传值调用
    • 2. 传址调用

一、const 使用指南

1. const修饰变量

众所周知,变量是可以修改的。但有时候,我们希望一个变量不能被修改,这就需要使用关键字const

#include<stdio.h>
int main()
{
int m = 12;
m = 24;   //m可以被修改
const int n = 12;  //使n不能被修改
n = 24;   //n不可以被修改
return 0;
}

如上,如果执意修改这个n,那么程序运行后,就会报错:

在这里插入图片描述但是我们绕过n,而利用n的地址去修改n,也能做到:

#include<stdio.h>
int main()
{
int m = 12;
m = 24;  
const int n = 12;
int* p = &n;
*p = 24;
printf("%d",n);
return 0;
}

在这里插入图片描述但是这一通操作下来,我不禁思考,const的目的本来就是防止变量被修改,但使用地址还是能修改它。const单纯修饰变量并不完善,应该让p拿到n的地址也不能修改n,那么该怎么做呢?
在这里插入图片描述

2. const修饰指针变量

我们可以让const修饰存有地址的指针变量,一般来讲,const可以写在*的左边,也可以写在它的右边,意义是不一样的。

int* p;
const int* p; //等价于 int const * p; 放在*的左边
int* const p; //放在*的右边
  • const放在*左边:限制*p的改变,即指针指向的地址的内容不能通过指针被改变了。但指针变量本身可以改变,即指针变量指向的地址可以改变。
int n=12;
int m=24;
const int* p=&n;
*p = 24;  //不可以(n的值不能改变了)
p = &m;   //可以的(让p存放m的地址)
  • const放在*右边:限制p的改变,即指针变量指向的地址不能改变了。但不限制*p的改变,即指针指向的地址的内容可以改变。
int n=12;
int m=24;
int* const p=&n;
*p = 24;  //可以的(把n的值改成24)
p = &m;   //不可以(不能让p存放的地址改变了)

当然,其实也可以在左右两侧都写上const,const int* const p = &n;意味着该指针指向的地址和该地址里的内容都不能改变了

二、野指针

1. 野指针的概念

一言以蔽之,野指针就是指向位置是随机的、不正确的、没有明确限制的指针,像“野狗”一样。

2. 野指针的成因

野指针有以下几个成因:

1.== 指针变量未初始化==:
int* p;,p存放的地址默认为随机值,p是野指针

  1. 指针越界访问
int arr[10]={0};
int* p = &arr[11];

指针指向的范围超过了数组的范围,p是野指针

  1. 指针指向的空间释放
int test()
{
int n = 12;
return &n;
}

int main()
{
int* p = test();
}

在上面的代码里,n是函数test里的一个局部变量,在离开test函数后变量n就被销毁了,它所占的内存空间(地址)也就被释放,传给p传了个寂寞,p就是野指针了。

3. 如何规避野指针

  1. 指针变量初始化

创建变量随即初始化是好文明,但如果在定义指针变量时暂且不知道它该指向哪里,可以赋值NULL,NULL是C语言中定义的一个标识符常量,值是0int* p = NULL;,后续知道了p该指向哪里时,再修改就行。

  1. 小心指针越界访问

  2. 避免返回局部变量的地址

  3. 指针变量不再使用时,置为NULL,指针使用前检查有效性
    NULL的值是0,0也是地址,但是这个地址是无法使用的。编程界有一个约定俗成的规则是:只要是NULL指针就不去访问,使用指针前判断指针是否为NULL。那么如何检测指针是不是NULL呢?可以用到下面的 assert()

在这里插入图片描述

三、assert(断言) 使用指南

C语言的宏是一种在预处理阶段进行文本替换的工具,主要用于定义常量、简化代码或执行简单的文本替换任务。宏的定义使用#define代码,例如#define pi 3.1415,这样下面的代码每次出现pi时,都会被替换成3.1415

在assert.h头文件里,定义了宏assert(),用于确保程序运行时符合指定条件,如果不符合程序就会报错终止运行,这个宏常常被称为“断言”,它接受任何一个表达式作为参数,如果该表达式为真(非0),assert()不会产生任何作用,程序正常运行;如果该表达式为假(0),assert()就会报错,并会写出没有通过的表达式,以及包含这个表达式的文件名和行号。

举个栗子:

#include<assert.h>
int main()
{
	int* p = NULL;
	assert(p != NULL);  //p是NULL,所以这个表达式为假
	return 0;
}

在这里插入图片描述assert()在实际编写程序是非常有用的,它能自动标出出问题的文件和行号。不仅如此,assert()还有一种关闭的方式,在程序员修完bug后认为不需要再做断言时,就可以在#include<assert.h>的前面,定义一个宏#define NDEBUG,它可以使编译器禁用掉程序里所有的assert()语句,防止引入额外的检查而增加运行时间。倘若运行时又双叒叕发现了bug,就把#define NDEBUG移除(或者注释掉),再次利用断言排查代码……直至程序没有问题。

在这里插入图片描述

四、传值调用和传址调用

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

举个狠简单的例子:写一个函数交换两个整数的值

1. 传值调用

很多人可能会写出这样一段代码:

#include<stdio.h>

void Swap1(int x , int y)
{
  int t = x;
  x = y;
  y = t;
}

int main()
{
int a,b;
printf("请输入a,b的值:");
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;
}

然鹅,结果不对
在这里插入图片描述没有产生交换的效果,为什么o(╥﹏╥)o
我们观察一下这几个变量的地址看看:
在这里插入图片描述可以看到,a和x的地址并不相同,b和y的地址并不相同,相当于a,b,x,y存放在独立的空间。实参传递给形参时,形参会单独创建一份空间来接受实参的值,而对形参的修改不影响实参。那么在Swap1函数内部只是交换了x和y的值,不会影响到a,b的值。

这种把变量本身传递给函数的调用,叫做传值调用

2. 传址调用

为解决这个问题,就可以使用指针了。在main函数里将a和b的地址作为参数传递给交换函数,交换函数里通过地址参数找到a和b,进而完成交换

#include<stdio.h>

void Swap2(int* x , int* y)
{
  int t = *x;
  *x = *y;
  *y = t;
}

int main()
{
int a,b;
printf("请输入a,b的值:");
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函数是将变量的地址传递给了函数,这就叫传址调用。传址调用,可以通过地址,让主函数和函数之间建立真正的联系,在函数内部可以修改主函数中的变量。

综上,函数只需要主函数的参数值来计算的话,使用传值调用足矣;如果函数内部要修改主函数中变量中的值的话,就需要使用传址调用。
在这里插入图片描述

欲知后事如何,且听下回分解~
本篇完,感谢阅读!

标签:const,变量,落羽,C语言,assert,int,地址,指针
From: https://blog.csdn.net/2402_86681376/article/details/143798744

相关文章

  • 什么是C语言中的指针?
    1.基本概念在C语言中,指针是一种变量,它存储的是另一个变量的内存地址。可以把内存想象成一个巨大的公寓楼,每个变量就像住在公寓里的居民,而指针就是写着居民房间号(内存地址)的纸条。例如,假设有一个整型变量a,它存储在内存中的某个位置,指针变量p就可以用来保存变量a的内存地......
  • c语言sizeof与strlen的区别详细解析
    char*p="abcdef";printf("%d\n",sizeof(p));p是指针变量(地址),地址就是地址,大小就是4/8字节printf("%d\n",sizeof(p+1));p+1是b的地址,还是地址4/8字节printf("%d\n",sizeof(*p));*p是‘a’,sizeof(*p)计算的是字符的大小,是1字节printf("%d\n"......
  • c语言——三子棋基础游戏
    首先,我们先有整体思路:它的棋盘到底是怎么样子的?它实现的过程是怎么一步一步来的棋盘到底是怎么样子的?大概是这么一个轮廓。实现过程中,我们该如何思考呢?1.我们想到,开始游戏的时候,我们肯定会一个菜单的吧?2.利用菜单选择是否开始游戏3.开始游戏后,我们就开始设计排版棋盘了:1......
  • 【C语言指南】C语言内存管理 深度解析
           ......
  • 关于我重生到21世纪学C语言这件事——指针详解(2)
    人无完人,持之以恒,方能见真我!!!共同进步!!文章目录1.数组名的理解2.使⽤指针访问数组3.⼀维数组传参的本质4.冒泡排序5.⼆级指针6.指针数组7.指针数组模拟⼆维数组1.数组名的理解在上⼀个章节我们在使⽤指针访问数组的内容时,有这样的代码:intarr[10]={1,......
  • c语言笔记(鹏哥)课件+上课板书汇总(深入指针1)
    深入指针(1)⽬录:一、内存和地址二、指针变量和地址三、取地址操作符四、指针变量类型的意义(这一讲到这)五、const修饰指针六、指针运算七、野指针八、assert断⾔九、指针的使⽤和传址调⽤内存和地址引例:假设有一个宿舍楼,你在一个房间里,宿舍楼里每一间房间都......
  • 数据结构程序设计(C语言)校园导游系统
    使用队列以及深度搜索算法,加上dos命令调用图片的校园导游系统#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>#include<string.h>#include<Windows.h>structgraph{ intnode_num;//顶点数 intedge_num;//边数 charnode_name[20][50......
  • C语言经典100题 学习笔记(更新中)
    第一题:有1、2、3、4四个数字,能组成多少互不相同且无重复数字的三位数?都是多少?#include<stdio.h>//有1、2、3、4四个数字//能组成多少互不相同且无重复数字的三位数?都是多少?intmain01(){ inta=0; intb=0; intc=0; intcount=0; for(a=1;a<5;a++) {......
  • C语言题目:求平方数(附代码和思路)
    编程思路://做到心中有数,代码看似无数,实则心中有数假设这个整数为X,则有X+100=n*n,X+100+168=m*m;可以得出n与m的关系是m*m-n*n=168;即(m+n)(m-n)=168;所以有设m+n=i,m-n=j;则i*j=168;我们可以使用for循环来遍历筛选i和j的值,条件的控制需要注意168%i==0才能赋值给j......
  • C语言进阶3:字符串+内存函数
    本章重点求字符串长度strlen长度不受限制的字符串函数strcpystrcatstrcmp长度受限制的字符串函数strncpystrncatstrncmp字符串查找strstrstrtok误信息报告strerror字符操作内存操作memcpymemmovememcmpmemset0.前言:C语言中对字符和字符串的处理很是......