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

【C语言入门】由浅入深学习指针 【第二期】

 

目录

 1. 指针变量为什么要有类型?

2. 野指针

2.1 未初始化导致的野指针

2.2 指针越界导致的野指针

2.3 如何规避野指针 

3. 指针运算

3.1 指针加减整数

3.2 指针减指针

3.3 指针的关系运算

4. 二级指针

5. 指针数组

5.1 如何使用指针数组模拟二维数组

        上一期已经解释了指针和指针变量的区别以及含义,这一期我们来具体了解一下指针到底有什么作用。

 1. 指针变量为什么要有类型?

        我们知道指针变量的大小是根据所在平台决定的,若平台是32位即x86环境,则指针变量是4个字节,若平台是64位,则指针变量是8个字节。既然指针变量不会根据指针类型变化,那么我们为什么要区分指针变量的类型呢?换句话说char* 和 int* 都有能力存储4个字节的地址,为什么不统一成一个自定义的指针变量例如ptr*

        从上图可以得知,我们把int*类型的地址强行赋给char*类型的地址,再进行取值调用的时候,会根据char*来进行赋值以至于只会将一个字节赋值成0,若是正常使用int*来接收,那么四个字节都是0 ,如下图所示。

        由下图可知,虽然不同指针变量的存储类型不影响地址的存储,但是当直接对地址进行运算,还是会根据指针类型的不同进行运算。char*类型+1,跳过1个字节,int*类型+1,跳过4个字节。

所以我们会得到这样的结论:指针变量的类型在存取地址的时候可有可无,在修改地址指向的内容的时候是非常有必要的,它决定了在利用地址更改值的时候需要更改多少个字节;若直接对地址进行运算,那么会根据指针变量的类型+‘1’,这个‘1’可能是char类型的一个字节,也可能是int类型的四个字节。 

使用float*和int*的时候,无论是访问内存还是使用内存,都是4个字节,那么可不可以混用呢?

答案是:不可以

下图是整型解引用赋值整型,我们可以看到100以16进制存入内存。

我们把100.0存入内存,发现内存存的数据发生了变化, 虽然都是100,但是浮点数存入内存的方式是不同的。

2. 野指针

        指针指向的内存没有初始化,或者已经被销毁,此时的指针称为野指针。

2.1 未初始化导致的野指针

2.2 指针越界导致的野指针

2.3 如何规避野指针 

①指针初始化。

②指针小心越界。

③指针指向的空间置为NULL。NULL是0,类型是void*,如果取值的话一定会报错(默认0地址不能访问),所以,我们可以先判断这个指针如果不是NULL再进行取值。

④避免返回局部变量的地址,因为局部变量已经被销毁了(没有那块内存空间的使用权限了)。

⑤指针使用之前检查有效性。

3. 指针运算

3.1 指针加减整数

这里的values[N_VALUES]并不算下标越界,因为并没有使用这段内存,只是用了一下这个地址作为判断条件。

3.2 指针减指针

当同类型的地址减去同类型的另外一个地址,得出的结果的绝对值是两个地址之间的元素个数

需要注意的是,两个指针指向的必须是同一块内存,不然毫无意义。

        那么有同学会想,指针+指针得到的结果是什么呢,这个结果毫无意义,类似于你可以把日期相减,得到天数,但是你不能把日期相加,这样就毫无意义

3.3 指针的关系运算

其实就是地址之间的比较,判断两个地址谁大谁小,从而对地址指向的内容进行操作。

4. 二级指针

        简单描述一下,就是指针变量里面存的地址,这个地址是一个指针的地址。乍一听是不是以为在说绕口令呢?下面用一张图来进行诠释。

        上图中表示int * pa = &a;我们可以这么理解:*号表明pa的类型是一个指针类型,int表明这个指针类型指向的是int类型;那么我们是不是可以来理解一下二级指针。

        int* *ppa = &pa;还是将类型拆开,距离ppa最近的*表明ppa是一个指针,int* 表明ppa指向的类型是int*的类型

总结:二级指针是用来存放一级指针变量的地址

5. 指针数组

主语是数组,即存放指针的数组就是指针数组

5.1 如何使用指针数组模拟二维数组

        首先构造出三个一维数组,所谓二维数组就是将这三个一维数组连起来;我们构造一个指针数组,分别存入之前构造的一维数组名,在之前的帖子说明,一维数组名是数组的第一个元素的地址,所以分别存入了三个地址,我们需要遍历指针数组,得到三个一维数组的首地址,再引入一个变量j,根据指针加减法,就可以访问一维数组中除了第一个元素的其他元素的地址,最后解引用就可以得到最后的结果。

#include <stdlib.h>
 

int main()
{
    int arr1[4] = {1,2,3,4};
    int arr2[4] = {2,4,6,8};
    int arr3[4] = {3,6,9,12};
// 用一维数组模拟出二维数组的效果
    int* arr_sum[3] = {arr1,arr2,arr3};
    for(int i = 0;i < 3;i++)
    {
        for(int j = 0;j < 4;j++)
        {
            printf("%d ",*(arr_sum[i] + j));
        }
        printf("\n");
    }
    return(0);
}

http://www.dtcms.com/a/109738.html

相关文章:

  • Sora结构猜测
  • TypeScript 元数据操作 API 及示例
  • Unity HDRP管线用ShaderGraph还原Lit,方便做拓展;
  • 详解Pinctrl子系统
  • Redis 的 Raft 选举协议
  • 使用MFC ActiveX开发KingScada控件(OCX)
  • Mac Apple silicon如何指定运行amd64架构的ubuntu Docker?
  • 浅浅尝试Numpy的函数:
  • 【数据结构】排序算法(中篇)·处理大数据的精妙
  • git commit Message 插件解释说明
  • Ubuntu服务器挂载之前的数据硬盘
  • C++中的链表操作
  • 网络运维学习笔记(DeepSeek优化版)027 OSPF外部路由计算
  • GaussDB数据库SQL开发实践与性能优化全解析
  • 汇编学习之《jcc指令》
  • Vue 数据传递流程图指南
  • CompletableFuture:核心方法、应用场景
  • 面向对象软件开发与中国哲学的关系
  • Java面试题及答案整理( 2025年 4 月最新版,持续更新)
  • 每天10分钟!自动抓取并生成每日简报的AI方案
  • 从零开始训练Codebook:基于ViT的图像重建实践
  • 认识 Promise
  • 三、GPIO
  • 【上新了】深入理解 Java 虚拟线程:与传统线程的对比及应用
  • Vue2 监听器 watcher
  • qt tcpsocket编程遇到的并发问题
  • Spring Boot应用中实现Jar包热更新的实践指南
  • macOS设置定时播放眼保健操
  • Java的数据库编程——JDBC基础
  • 2025年AI技术十大趋势深度解析:从实验室到主流应用