当前位置: 首页 > news >正文

《重生到现代之从零开始的C语言生活》—— 指针2

const

const修饰变量

指针可以解引用修改变量,如果我们不想让它被修改可怎么办啊
这个就是const的作用

int main()
{int a = 0;a = 10;printf("%d",a)const int b = 0;b = 11;printf("%d",b);return 0;
}

在此代码中,a是可以被修改的,b是不能被修改的,因为b被const修饰从语法规则(记住是语法规则,不是把他变成了常量)上入手,给他做了限制,修改就不符合语法规则,导致没有办法直接修改变量

但是我们还是可以绕过语法规则,从地址出发改变变量

int main()
{const int a = 0;int * n = &a;*n = 10;return 0;
}

此举就是绕过语法规则,从地址上更改变量,那怎么样可以不让他从地址改变变量呢

const修饰指针

const修饰指针有三种情况

int const * a = 0;//const在*左边
int * const a = 0;//const在*右边
int const * const a  = 0;//const在*左边和右边

那么他们分别代表什么呢?

int const * a = 0;

const在*的左边,修饰的是指向的内容,就代表着指向的内容不能通过指针来改变,但是指针变量本身可以改变(可以这么理解,const的后面是解引用a,就代表了修饰的是解引用a,也就是指向变量,所以可以修改储存在指针变量里面的地址,但是不能通过解引用来修改指向的变量

int * const a = 0;

const在*的右边,修饰的是变量,就代表着指针变量本身不能修改,但是指向的内容可以被修改(const后面是a,就是修饰指针变量,所以不能修改变量本身)

如果指针变量和指向内容都不想被修改,那简单,*左右两边都写一个const不就行了(滑稽)

指针运算

指针基本运算分为三种

  • 指针加减整数
  • 指针减指针
  • 指针的关系运算

指针加减整数

指针加减整数,就是将指针的往后或往前移动整数倍的指针变量类型的字节

指针减指针

指针减指针所得的是两个指针之间的元素个数

#include<stdio.h>
int main()
{
int arr [10] = {1,2,3,4,5,6,7,8};
int * a1 = &arr[0];
int * a2 = &arr[4];
printf("%d",a1 - a2);
return 0;//运行结果为4
}

指针的关系运算

指针之间的关系也可以理解为地址和地址之间的关系
比如指针大小的比较

运用如下

#include<stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8 };int* p = &arr[0];while (p < arr + 10){printf("%d", *p);p++;}return 0;
}

野指针

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

野指针的形成有下面几种原因

  • 指针未初始化(未初始化,默认随机值)
  • 指针越界访问(指针指向的范围超出了原规定指向的范围)
  • 指针指向的空间释放(指函数中的变量或局部函数的空间释放,指针无法指向,造成野指针)

想要规避野指针,我们就要从根源出发

  • 指针初始化(知道指向哪里就赋值,不知道指向哪里就给指针赋值NULL
  • 小心指针越界
  • 避免返回局部变量的地址
  • 指针变量不再使用时,及时置NULL,指针使用之前检查有效性

NULL是C语言中定义的一个标识符常量,值是0,0也是地址,但这个地址是无法使用的,读写该地址会报错,就能很好的避免野指针的情况发生

assert

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

assert(p != NULL)

在代码运行到这一行的时候,会验证变量p是否等于NULL,如果不等于,程序会继续运行,否则的话会报错
assert()不仅能自动标识文件和出问题的行号,还无需修改代码就能关闭。当确定程序没问题时,在#include<assert.h>语句的前面,定义一个宏NDEBUG

#define NDBUG
#include <assert.h>

一般可以在Debug版本使用,在Relesse版本中禁用就可以

传值调用和传址调用

写一个函数,交换a和b的值

#include<stdio.h>
void swap(int a, int b)
{int t = 0;t = a;a = b;b = t;
}
int main()
{int a = 2;int b = 3;printf("%d ,%d\n",a, b);swap(a, b);printf("%d ,%d",a, b);return 0;
}

我们在main函数的内部,创建了a和b,在调用swap时将a和b传递给swap函数中的形式参数,函数中形式参数确实接受了a和b的值,但是形式参数有他们自己的地址,意思是,形式参数只是有了a和b值的参数,因为地址不一样,所以怎么改变形式参数的值和main函数中的a和b都没有关系

这就是传值调用

那么直接将a和b的地址传给swap函数不就行了

#include<stdio.h>
void swap(int * a, int * b)
{int t = 0;t = *a;*a = *b;*b = t;
}
int main()
{int a = 2;int b = 3;printf("%d ,%d\n",a, b);swap(&a, &b);printf("%d ,%d",a, b);return 0;
}

这就是传址调用
把地址传给函数就能从根本实现函数值的改变


今天的知识讲解完啦,如果觉得有用可以点一下赞和关注,也可以先收藏以防需要时找不到哦,当然如果作者写的哪里有问题欢迎指出,我们一起进步!!!

祝看到这里的人天天开心哦(笔芯)


http://www.mrgr.cn/news/2941.html

相关文章:

  • 【JavaScript 】JavaScript 全教程 02
  • python从列表中选出最后4个元素
  • 【Leetcode:93. 复原 IP 地址 + dfs】
  • 帆软报表,达梦数据库驱动上传失败
  • 自然语言处理NLP四范式
  • ArrayList与顺序表
  • ADB使用报错的问题FileNotfoundError:[WinError 3]系统找不到指定的路径
  • 跟李沐学AI:目标检测的常用算法
  • 怎么在网络攻击中屹立不倒
  • 自闭症学校一年学费多少?
  • 网络协议(概念版)
  • 独家揭秘丨GreatSQL 的MDL锁策略升级对执行的影响
  • 深度学习(9)---ResNet详解
  • ant design 的 tree 如何作为角色中的权限选择之一
  • 【大模型】triton inference server
  • 仿Muduo库实现高并发服务器——任务定时器模块
  • 如何在Sui上进行质押
  • PXE-Kickstart高效批量装机
  • axios的使用
  • 玩机进阶教程-----回读 备份 导出分区来制作线刷包 回读分区的写入与否 修改xml脚本