C语言————深入理解指针1(通俗易懂)

        C语言越学到后面,越会感到恐慌,听到指针、结构体等等这些,想必很多人不自觉的就会感觉很难,就想打退堂鼓了。哈哈哈哈,被小博猜到了吧!!悄悄告诉你们,小博刚开始学习的时候也是。但是时过一年,小博又是一条好汉,又重新回来拾起了这个曾经把我吓得连连后退的语言了。接下来,跟着小博来学习指针吧!!

一、内存和地址

        指针是什么,这里小博先给你们打个预防针,指针==内存单元的编码==地址,首先你应该接受这个概念。好了,那么内存单元又是什么?内存单元的地址有什么用?

       我们给计算机输入数据,计算机都是要通过内存存到一个空间里面的,等我们需要的时候,计算机再从内存中取出数据供我们使用。数据很多时,计算机如何才能准确的找到我们所需的数据呢?可以把内存看成一栋大楼,里面有很多个房间,每个房间都有门牌号,计算机每次把读到的数据存入对应的房间,但需要的时候,计算机会自动的通过门牌号找到该房间所存的数据。每个房间就是内存单元门牌号就是内存单元的编码,即地址

        听“都瑞咪发嗦啦”,钢琴被谁按动了。你有没有想过为什么每个键能发出不一样的声音,并且弹奏者能够准确定位到每个琴弦的位置?是的,这些都是被制造商提前在硬件层面上设计好的。然而计算机也一样,计算机也是由各种各样的硬件组成,各个硬件之间协调工作,使得他们之间可以相互进行数据传递。学过计算机组成原理的同学都知道,计算机的CPU和内存之间存在大量的数据传输,并且通过“线”进行传输。这里小博带你更深的了解计算机里的内存。       

        计算机中内存可划分为一个一个内存单元,每个内存单元的大小为1个字节,一个字节里有8个比特位其中每个内存单元都有各自的编号。

补充

  • 1 byte(字节)= 8 bit
  • 1 KB = 1024 byte
  • 1 MB = 1024 KB
  • 1 GB = 1024 MB
  • 1 TB = 1024 GB
  • 1 PB = 1024 TB

        计算机中CPU和内存之间通过线进行数据交互,主要有如上三类。当然这里我们主要讲地址线,即CPU是通过地址线找到内存单元的编码的。进行数据传输的时候,肯定不会用一根地址线了,那样多慢了,计算机在被制作的时候,是会被设定好有多少根地址线进行传输的,这些地址线统称为地址总线。如:32位的机器有32根地址线,1根地址线只有0或1两种状态,表示电脉冲有无。依次类推,2根地址线有4种状态,3根有8种.......32根地址线就有2^32种状态每种状态都代表一个地址,就可以通过该地址找到对应的数据。

二、指针变量和地址

1、指针变量

我们的重头戏来了!!首先要弄清楚两概念:

指针:指的是地址

指针变量:是指存放地址的变量

我们通过上面已经知道了什么是地址,这只是一个概念,我们要怎么用计算机语言将地址表示出来呢,当然要通过一个变量表示了,即指针变量

int a = 10;
printf("%p\n", &a);		//&a-----&取地址操作符,单目操作符
int* p = &a;		//p是一个变量(指针变量),是一块空间,表示取出a的地址放到p中去

这里要注意,指针变量p的类型int **相当于指针变量p的标识符int 是指p中存放的地址对应的变量a是int类型,记住一句话,指针变量中的内容就是地址。

2、取地址操作符(&)

上面我们学的 int* p = &a; 是对指针变量的定义,其中&取地址操作符,用来遍历指针变量,

&a表示对a进行取地址

如:

int a = 10;
printf("%p\n", &a);		//&a-----&取地址操作符,单目操作符
int* p = &a;		//p是一个变量(指针变量),是一块空间,表示取出a的地址放到p中去

其中a为整形变量,会向内存空间中申请4个字节,且每个字节都有对应的地址。

然而,printf("%p\n", &a); 打印出的则为:0x006FFD70,即取出的为4个字节中地址较小的字节的地址。

                   

总结&a取出的是a所占4个字节中地址较小的字节的地址

    而我们口头语中说的p是一个指针,其实指的是指针变量,只是我们通常习惯这么说而已。

    3、解引用操作符(*)

    上面我们讲到将变量的地址存起来,当然我们也得找到他。那么问题来了,内存空间那么大,内存单元那么多,指针变量如何才能精准的定位到目标地址?这时候就要用到解引用操作符(*)了,如:

    int a = 10;
    printf("%p\n", &a);		//&a-----&取地址操作符,单目操作符
    int* p = &a;		//p是一个变量(指针变量),是一块空间,表示取出a的地址放到p中去
    *p;     //*---解引用操作符(间接访问操作符)

    也就是说在指针变量的旁边放上一个*,就可以准确的找到a的地址

    如上,我们通过 *p 找到a的地址之后,将其中的变量a改为0,则打印出的结果就为0。

    再次梳理一下上面讲的1、2、3:   

    &a :将整形变量a的地址取出。

    p = &a;   将a的地址取出存放到变量p中,故p称为指针变量

    int* p = &a

    • int* 指针变量p的类型
    • 相当于指针变量p的标识符,
    • int 是指p中存放的地址对应的变量a是int类型

    *p:找到整形变量a的地址。

    其中,和 * 相当于一对的,一个取出,用于存放;一个找到,用于解引用。

    三、指针变量的类型

    1、指针变量的大小

    64位的机器中有64根地址线,这64根地址线中,每一个地址都是由64个二进制位组成的二进制序列,存储起来就需要8个字节的空间,指针变量的大小就是8个字节和类型无关

    如,在x64(64位)环境下运行的结果

    如,在x86 (32位) 环境下运行的结果

    总结:

    • 32位平台下地址是32个bit位,指针变量大小是4个字节。
    • 64位平台下地址是64个bit位,指针变量大小是8个字节。
    • 指针变量的大小和类型无关,在相同的平台下,指针大小都是相同的。

    2、指针变量类型的意义

            有没有发现,指针变量无论是哪种类型,在同一平台下,其大小都是一样的,那么定义这些类型又有什么意义呢?

            来看一下这里两个指针变量的区别,同样是对整形变量a进行取地址,int * 和char * 申请到的内存空间的大小一样大,即指针变量的大小相同。但当对指针变量进行解引用操作的时候,int * 改变了内存空间中的4个字节,而char *则改变了内存空间中的1个字节

    所以说,指针类型决定了指针进行解引用操作的时候访问了几个字节,也就是决定了指针的权限

    3、指针 + - 整数

            当我们定义两个指针变量类型,int * 和char *,分别对其进行+1操作,可发现int *类型的指针变量跳过了4个字节char *类型的指针变量只跳过了1个字节

    因此,指针类型决定了指针进行+1,-1的时候,一次跳过的距离。

    • int * + 1 ——>跳过4个字节
    • char * + 1——>跳过1个字节

    4、void *指针

            根据上文,我们了解到指针变量有很多类型,如:char *,int *,short *等等类型。然而,这里有一种特殊的类型void *类型,可以理解为无具体类型的指针(泛型指针),可以接受任意类型的地址,但无法进行指针的+-整数和解引用运算一般用于,当你取出了一个变量的地址,但不确定放在什么类型的指针变量中,就可以用void *这种类型来替代这个不确定的指针变量的类型。

    这里我们不着重讲了。

    四、const修饰指针

    看到const,你是否似曾相识呢?是的,我只知道他是一个关键字,平常我也没用过啊!!😥😥

    哈哈哈哈,没关系的,小博带你再温习一遍。

    1、const修饰变量

    const常属性(不能被修改的属性),通俗点说,就是一个变量被const修饰后,该变量的值就不能被修改了。如:

    #include <stdio.h>
    int main()
    {int a = 10;const int b = 10;a = 20; //a是可以修改的b = 20; //b是不能被修改的printf("%d %d\n", a, b);return 0;
    }

            如上,被const修饰后的b是不能被修改的,在语法上加了限制,只要我们在代码中对b进行修改,编译器就会报错,致使无法直接修改b

            const int b=10;  这里我们要知道,const修饰了变量b,使b具有了常属性,不能被修改了,但b本质上还是变量,称常变量在VS中,c++语言编译模式下,常变量就是变量,即使b被const修饰,b的值依旧可以被修改)。怎么用呢?当你有一个变量,不想让别人修改的时候,可以直接用const修饰。

            然而,天无绝人之路,你说改不了,我就偏要改!!哈哈哈哈,固执的大学生是这样的。还别说,真有办法,虽然被const修饰后的变量不能被直接修改,但是可以通过找到改变量的地址,用解引用操作,将其中的数值修改如下:

    2、const修饰指针变量

    和普通变量相比,const在修饰指针变量时稍有不同,首先我们需要先理解这张图:

     

            大家一定要把上面的图中各变量的关系搞清楚,首先是有一个变量a中存放的值为10指针变量p中存放的是变量a的地址,然而指针变量p也有自己的地址

    理解了上面的关系,我们再来看const在修饰指针变量时有什么不同,

    首先,位置不同:

    1、const 放在 *右边

    int* const p = &a;
    

    和修饰变量很相似,被const修饰过的指针变量依旧无法直接被修改。

    依旧相似,如果想改变被const修饰过的指针变量,同样可以用解引用操作

    如此以来,当const放在*右边修饰指针变量时,其用法和修饰变量的相似。

    然而我们要知道他的意义,const放在*右边修饰指针变量时,const限制的是指针变量p本身,指针变量不能被修改了,但是可以通过指针变量,修改指针变量指向的内容

    2、const 放在 *左边

    int const * p = &a;
    

            有了上面的基础,该代码就更好理解了,我们直接来总结一下,const在修饰指针变量,放在*的左边,限制的是指针变量的内容,不能通过指针来修改指向的内容,但是可以修改指针变量本身的值(修改指针变量的指向)

    3、* 前后都放const

    如果是int const * const p这种形式,以上两种情况都会受到限制。

    总结来说,const只限制其后面的的内容,共有三种类型:

    int * const p  ——>  p = &n ; //err 

    int const * p  ——>  * p = 100 ;  //err

    int const * const p ——>

    • *p = 100 ; //err
    • p = &n ; //err 

    五、指针运算

    1、指针+、- 整数

    其实在上面的讲解中,我们已将了解过了指针的+、- 运算。如:

    int a=10;
    int * p = &a;
    printf("%d\n", p+1);
    • int * p ——> p + 1 跳过4个字节
    • char * p ——> p + 1 跳过1个字节
    • type * p ——> p + 1 跳过 1*sizeof( type )个字节
    •                         p + n 跳过 n*sizeof( type )个字节

    2、应用举例

    例:给定一个数组,将其打印出来

    方法一:

    按照正常的思路,我们会想到用for循环的方式打印:

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

    方法二:

    当然我们也可用指针的方法:

    #include <stdio.h>
    int main()
    {int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int i = 0;int* p = &arr[0];int sz = sizeof(arr) / sizeof(arr[0]);	//计算数组中元素的个数for (i = 0; i < sz; i++){printf("%d ", *(p+i));    //解引用操作}return 0;
    }

    方法三:

    对于方法二稍做修改:

    #include <stdio.h>
    int main()
    {int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int i = 0;int* p = &arr[0];int sz = sizeof(arr) / sizeof(arr[0]);	//计算数组中元素的个数for (i = 0; i < sz; i++){printf("%d ", *p);    //解引用操作p++;}return 0;
    }

    其实,当我们熟悉了数组存放的底层逻辑之后,并不难写出如上代码。即数组在内存中是连续存放的。

    3、指针-指针

    指针1 + 整数 == 指针2

    指针2 - 指针1 == 整数

    即:指针 - 指针的绝对值 ——> 得到的两个指针元素之间的个数 

    指针- 指针计算的前提条件是:两个指针指向的必须是同一块空间

    printf("%d ",&arr[9]-&arr[0]);

    4、指针的关系运算

    指针和指针比较大小

    举个例子来说,依旧用上面的例题:给定一个数组,将其打印出来

    方法四:

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

    这又是一种新的方法,利用了指针的运算关系,打印数组。

    这些方法都比较新颖,还需我们多多积累,反复复盘,深入理解指针和内存的底层逻辑

            好了,关于指针的基础知识,小博先讲这么多,指针涉及到的知识点还有很多,小博还会陆续更新,因为小博也是新手,所以哪里有不对的地方还请多多指教。如果你也是小白,那么跟着小博一起来学习吧!!给自己几天时间,一定可以学会的,加油!!

    这里小博送给大家自己喜欢的一句话:“言语压君子,衣冠镇小人”。

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
    如若转载,请注明出处:http://www.pswp.cn/web/95178.shtml
    繁体地址,请注明出处:http://hk.pswp.cn/web/95178.shtml

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    香港电讯为知名投资公司搭建高效、安全IT管理服务体系

    客户背景 客户为一家世界知名的能源投资公司在中国设立的子公司&#xff0c;在中国拥有涵盖煤炭开采、火力发电、新能源以及能源贸易等贯穿整个能源供应链的业务体系&#xff0c;投资共计2个煤矿、4个电厂&#xff0c;以及7个光伏电站。 客户需求 客户希望通过位于北京的总部…

    紧急安全通告:多款 OpenSSH 与 glibc 高危漏洞曝光,CVE-2023-38408 等须立即修复

    概述&#xff1a;OpenSSH&#xff08;OpenBSD Secure Shell&#xff09;是加拿大OpenBSD计划组的一套用于安全访问远程计算机的连接工具。该工具是SSH协议的开源实现&#xff0c;支持对所有的传输进行加密&#xff0c;可有效阻止窃听、连接劫持以及其他网络级的攻击。 OpenSSH …

    随时随地开发:通过 FRP 搭建从 Ubuntu 到 Windows 的远程 Android 调试环境

    你是否曾梦想过这样的工作流:在咖啡馆里,你只带着一台轻薄的 Surface Pro,而代码的编译、运行和调试,全部交由家里那台性能强劲的 Ubuntu 台式机来完成?更酷的是,你甚至想将手机直接插在 Surface 上,让远端的 Ubuntu 无缝识别并进行开发。 今天,我们就将这个梦想变为现…

    异步编程与面向对象知识总结

    文章目录原型链关键字总结原型对象:prototype对象原型:__ proto__面向对象编程封装抽象多态总结异步编程基础循环宏任务嵌套微任务原型链关键字总结 原型对象:prototype 函数的属性,指向一个对象&#xff0c;这个对象是通过该函数作为构造函数创建的所有实例的原型 修改原型会…

    Spring Boot + KingbaseES 连接池实战

    文章目录一、前言二、什么是数据库连接池&#xff1f;三、SpringBoot KingbaseES 环境准备3.1 加依赖&#xff08;pom.xml&#xff09;3.2 基础连接信息&#xff08;application.yml&#xff09;四、四类主流连接池实战4.1 DBCP&#xff08;迁移型 / 传统项目友好&#xff09;…

    矩阵待办ios app Tech Support

    Getting Support: mail: 863299715qq.com

    React中优雅管理CSS变量的最佳实践

    在现代前端开发中&#xff0c;CSS变量&#xff08;也称为CSS自定义属性&#xff09;已成为管理样式系统的重要工具。它们提供了强大的动态样式能力&#xff0c;但在JavaScript中高效地访问和使用这些变量却存在一些挑战。本文将介绍一个优化的解决方案&#xff0c;帮助你在Reac…

    智能制造——解读装备制造业智能工厂解决方案【附全文阅读】

    适应人群为装备制造企业(如汽车、航空航天、能源装备等)中高层管理者、生产运营负责人、IT 部门(智能制造 / 工业互联网团队)、安全管理专员及园区数字化建设决策者。主要内容围绕装备制造业智能工厂解决方案展开,核心包括建设背景(解决生产安全管理缺失、工序手工记录无…

    macos调用chrome后台下载wasm-binaries.tar.xz

    实现脚本: down_wasm.sh DOWNLOAD_DIR="$HOME/Downloads" TARGET_FILE="wasm-binaries.tar.xz" TAG="32b8ae819674cb42b8ac2191afeb9571e33ad5e2" TARGET_DIR="$HOME/Desktop/sh/emsdk_setup/emsdk_deps"echo "下载路径: $DOW…

    【Proteus仿真】按键控制系列仿真——LED灯表示按键状态/按键控制LED灯/4*4矩阵键盘控制LED

    目录 1案例视频效果展示 1.1例子1&#xff1a;LED灯表示按键状态(两种方式) 1.2例子2&#xff1a;按键控制两排LED小灯闪烁移位 1.3例子3&#xff1a;按键控制LED灯逐个点亮/分组点亮/全部熄灭 1.4例子4&#xff1a;4*4矩阵按键实现带状LED灯控制 2例子1&#xff1a;LED灯…

    829作业

    用fgets&#xff0c;fputswanc代码#include<myhead.h> int main(int argc, const char *argv[]) {FILE *fp1 NULL;FILE *fp2 NULL;if (argc ! 3){printf("输入不合法:./a.out lydf.txt l.txt\n");return -1;}if ((fp1fopen(argv[1],"w"))NULL){pri…

    CRMEB小程序订阅消息配置完整教程(PHP版)附常见错误解决

    登录小程序后台 1.进入微信公众平台、小程序后台&#xff1a;功能->订阅消息。&#xff08;如未开通&#xff0c;点击申请即可开通&#xff09; 选择服务类目 2.选择服务类目&#xff1a;生活服务/百货/超市/便利店 同步小程序订阅消息 3.商城后台设置->消息管理 点击…

    【已解决】阿里云服务器上前端访问不到后端

    最开始我觉得后端根本没跑起来&#xff0c;但是我没用过阿里云的服务器&#xff0c;对pm2指令也完全不熟&#xff0c;不确定后端是不是在哪个我不知道的地方跑着。 还以为在阿里云控制台点运行&#xff0c;服务就会自己跑起来&#xff0c;但远程连接之后发现搞着搞着&#xff0…

    分治算法详解:从递归思想到经典应用实战

    分治算法是计算机科学中最重要的算法设计策略之一&#xff0c;它将复杂问题分解为规模更小的同类子问题&#xff0c;通过递归求解子问题并合并结果来解决原问题。本文将深入探讨分治算法的核心思想、设计模式以及经典应用案例。 文章目录一、分治算法核心思想1.1 分治策略的三个…

    GitHub 热榜项目 - 日榜(2025-08-31)

    GitHub 热榜项目 - 日榜(2025-08-31) 生成于&#xff1a;2025-08-31 统计摘要 共发现热门项目&#xff1a;15 个 榜单类型&#xff1a;日榜 本期热点趋势总结 本期GitHub热榜凸显三大技术热点&#xff1a;1) AI基础设施爆发式增长&#xff0c;微软MCP协议和Activepieces的A…

    OpenCL C 平台与设备

    1. 核心概念在 OpenCL C API 中&#xff1a;平台 (Platform)&#xff1a;代表一个 OpenCL 实现&#xff0c;通常对应硬件厂商&#xff08;NVIDIA、AMD、Intel等&#xff09;设备 (Device)&#xff1a;具体的计算硬件单元&#xff08;GPU、CPU、加速器等&#xff09;上下文 (Con…

    R语言贝叶斯方法在生态环境领域中的高阶技术应用

    贝叶斯统计已经被广泛应用到物理学、生态学、心理学、计算机、哲学等各个学术领域&#xff0c;其火爆程度已经跨越了学术圈。一&#xff1a; 1.1复杂数据回归&#xff08;混合效应&#xff09;模型的选择策略 1&#xff09;科学研究中数据及其复杂性 2&#xff09;回归分析历史…

    学习笔记:MySQL(day1)

    DDL&#xff08;Data Definition Language&#xff0c;数据定义语言&#xff09;是 SQL 语言的一部分&#xff0c;用于定义和管理数据库中的数据结构&#xff0c;包括创建、修改、删除数据库对象&#xff08;如数据库、表、视图、索引等&#xff09;。常见的 DDL 语句及其功能&…

    C++ 模板初阶:从函数重载到泛型编程的优雅过渡

    &#x1f525;个人主页&#xff1a;爱和冰阔乐 &#x1f4da;专栏传送门&#xff1a;《数据结构与算法》 、C &#x1f436;学习方向&#xff1a;C方向学习爱好者 ⭐人生格言&#xff1a;得知坦然 &#xff0c;失之淡然 文章目录前言一、引言&#xff1a;函数重载的痛点与模板…

    从零开始的python学习——语句

    ʕ • ᴥ • ʔ づ♡ど &#x1f389; 欢迎点赞支持&#x1f389; 个人主页&#xff1a;励志不掉头发的内向程序员&#xff1b; 专栏主页&#xff1a;python学习专栏&#xff1b; 文章目录 前言 一、顺序语句 二、条件语句 2.1、什么是条件语句 2.2、语法格式 2.3、缩进和代码…