C语言:指针详解-程序员宅基地

技术标签: c语言  

1、C语言指针是什么?

在C语言中,指针是一个非常重要的概念,简单的说:指针是存储空间的地址,计算机的存储空间由一个个的字节(8位)组成,指针就是这些字节的编号,通过这个编号,就可以访问到特定的存储空间。但是,指针如果简单的来说,就没办法简单的理解,接下来,我们将前面的描述换一种方式进行描述。

首先,我们明确一点,在计算机内,所有在存储空间内的数据都是或多或少的 0/1 位流组成,再往上点,可以说所有的数据都是以字节中的 0/1 序列形式存在,无论是代码数据、音视频、文档,在存储空间内并没有本质区别,都是 0/1 字节序列,那么,怎样让相同的数据具有不同的表现形式?其中的关键就是对数据的解释规则

同样一个苹果,在中国它写成:苹果,在英国写成:apple,变的不是这个水果,而是对它进行解释的规则,利用不同的规则对它进行解释会展现出不同的表现形式。

每个人都可以定义自己的规则来解释同一个数据,我们平时所说的标准、规范,不过也就是一些应用更为广泛、更具解释效率同时也是大家约定俗成的一套数据解释规则而已。C语言,也是一套解释规则,这套规则用来帮助我们更有效的与计算机进行交流,除了怎么对数据进行解释,另外一个关键就是数据本身是什么,由此可以得到两个关键的东西:数据本身是什么,以及怎么解释这个数据

2、C语言指针初体验

思考下列代码:

#include <stdio.h>

int cnt; 
int* cnt_ptr;

int main(int argc, char* argv)
{
    
    cnt = 2155905152;
    cnt_ptr = &cnt;

    printf("cnt = %d, cnt_ptr = %p\n",cnt,cnt_ptr);
}

首先我们看int cnt,按照C语言的规则,也就是语法,这句话表示向计算机申请一个变量,这个变量叫cnt(存储空间别名:别名是为了便于人类自己记忆),同时,我对它的访问需要按照int的规则进行,如果在本机上,int数据类型有四个字节,那么这句话的最终意思就是需要计算机为我准备四个字节的存储空间,同时将存储空间的别名叫做cnt,以后我叫到cnt,计算机就知道我叫的是这四个字节的空间,同时,对这四个字节空间中的 0/1 字节序列按照int的解释规则进行解释;

看到这里我知道你应该很困惑,如果实在搞不清楚,你只需要简单的理解:C语言语法全是规则,自己定义的变量全是别名;

接下来看下一句:int* cnt,规则是:int*,这告诉计算机我们申请的这个变量是拿来存储地址的,而且存储的还是一个int数据类型的地址;

cnt = 2155905152: 叫到了cnt存储空间,=规则告诉计算机把右边的这个数据2155905152放到前面这个叫cnt的地址空间里面去;

cnt_ptr = &cnt :叫到了cnt_ptr存储空间,&在C语言中表示获取它后面存储空间的空间首地址(第一个字节的空间编号),=规则告诉计算机把cnt空间的空间地址给放入到叫cnt_ptr的存储空间里面去;

到此为止,叫cnt的存储空间里面放的是:2155905152,叫cnt_ptr的存储空间里面放的是cnt存储空间四个字节中第一个字节的地址空间编号;

编译运行:

	gcc -o ctest ctest.c
	./ctest

输出结果:
在这里插入图片描述
诶?为什么cnt的输出是负数?我赶紧查了查四字节int的数据范围:-2147483648 到 2147483647,原来我们给它的赋值超出了它最大值;你还会发现,你的第二个地址输出和我也不一致,甚至运行两次的结果都不一样,这不必惊讶,只是操作系统耍的一点点小心机而已,致辞,我们才终于有了一个小例子,离理解还差得远,那么,再探指针!

3、再次体验C语言指针

代码如下:

#include <stdio.h>
#include <stdint.h>

uint32_t cnt;
int* cnt_ptr;

int main(int argc, char* argv)
{
    
    cnt = 2155905152;
    cnt_ptr = &cnt;

    printf("cnt = %d, cnt_ptr = %p\n",cnt,cnt_ptr);
}

和上一段代码唯一的不同是这次我们想要以4字节无符号数的方式对cnt里面的数据进行解释,也就是说我们修改了这段内存中数据的解释规则,再次运行查看结果:
在这里插入图片描述
为什么还是负数?仔细一看,原来在输出的时候还有一次数据解释规则的应用:%d表示输出的是整形,对无符号数的输出需要用到%u,修改输出语句为:

printf("cnt = %u, cnt_ptr = %p\n",cnt,cnt_ptr);

结果如下:
在这里插入图片描述
终于,我们输出了正常的数据,我们可以发现,不同的数据解释规则可以把相同的数据解释为不同的表现形式,至此,你是否有了切身体会?理解了数据本身和数据解释规则,接下来我们继续改造这段代码,进入指针的深入学习。

改造代码如下:

#include <stdio.h>
#include <stdint.h>

uint32_t cnt;
uint8_t* cnt_ptr;
uint32_t* tmp_ptr;

int main(int argc, char* argv)
{
    
    cnt = 2155905152;
    cnt_ptr = &cnt;
    tmp_ptr = &cnt;
    printf("cnt = %u, cnt_ptr = %p, tmp_ptr = %p\n",cnt,cnt_ptr,tmp_ptr);
}

相比于上一段代码,cnt_ptr的规则被换成了uint8_t*,这个规则表示cnt_ptr将被解释为一个一字节无符号数存储空间的首地址,这个是它的解释规则,那它的数据本身是什么?cnt_ptr = &cnt告诉我们它的数据本身是cnt存储空间的第一个字节的空间编号,同时,我们还声明了一个tmp_ptr,其本身数据也是cnt存储空间的第一个字节的空间编号,那真的是这样的吗?

编译运行:

在这里插入图片描述
确实如此,它俩的本身数据就是一样的,那不同的解释规则有什么不同呢?

改造代码如下:

#include <stdio.h>
#include <stdint.h>

uint32_t cnt;
uint8_t* cnt_ptr;
uint32_t* tmp_ptr;

int main(int argc, char* argv)
{
    
    cnt = 2155905152;
    cnt_ptr = &cnt;
    tmp_ptr = &cnt;
    printf("cnt = %u, cnt_ptr = %p, tmp_ptr = %p\n",cnt,cnt_ptr,tmp_ptr);
    for (int i = 0; i < 4; i++)
    {
    
        printf("i = %d *cnt_ptr = %u\n",i,*cnt_ptr);
        cnt_ptr ++; 
    }
}

在C语言中*cnt_ptr规则是:先拿到cnt_ptr这个存储空间里面的值(cnt变量的第一个字节的空间地址),把拿到的值解释为地址空间编号,再去这个地址空间编号所对应的空间里面拿出数据来,怎么拿?cnt_ptruint8_t*类型,那就一个字节一个字节的拿,cnt变量有四个字节,那么就可以拿四次才能把cnt的四个存储空间拿完,编译运行如下:
在这里插入图片描述
我们拿了四次,每次都是128,其二进制是:10000000,每个字节里面的数据都是这个,四个字节就是:2155905152,这是计算机按照我们给出的规则一个一个字节拿的结果。

tmp_ptrcnt_ptr的值一致,是否也可以这样操作呢?可以,但是它只能拿一次,因为它一次拿四个字节,一下就拿完了。

到这,你是否对指针有了初步的认识?是否理解到了数据本身和数据解释规则的重要性,要是还没有弄懂,我们可以试着拆解C语言里面的规则,灵活的组合各种C语言规则以实现自己需要的目的,这很灵活,但是这也是C语言指针的魅力;

4、拆解C语言二级指针

你也许通过,二级指针是指向指针的指针,那么你也许会写下如下的代码:

#include <stdio.h>
#include <stdint.h>

uint32_t cnt;
uint32_t* cnt_ptr;
uint32_t** cnt_ptr_ptr;

int main(int argc, char* argv)
{
    
    cnt = 2155905152;
    cnt_ptr = &cnt;
    cnt_ptr_ptr = &cnt_ptr;
    printf("cnt = %u, cnt_ptr = %p, cnt_ptr_ptr = %p ",cnt,cnt_ptr,cnt_ptr_ptr);
    printf("*cnt_ptr = %u, **cnt_ptr_ptr = %u\n",*cnt_ptr,**cnt_ptr_ptr);
}

编译运行这个代码:
在这里插入图片描述
呵!二级指针,不过是C语言吓退初学者的小把戏而已,你只需要牢记:数据本身和数据解释规则;
上面的代码进行如下改造,使用C语言指针的规则拆解掉二级指针!修改代码如下:

#include <stdio.h>
#include <stdint.h>

uint32_t cnt;
uint32_t* cnt_ptr;
uint64_t cnt_ptr_ptr;

int main(int argc, char* argv)
{
    
    cnt = 2155905152;
    cnt_ptr = &cnt;
    cnt_ptr_ptr = &cnt_ptr;
    printf("cnt = %u, cnt_ptr = %p, cnt_ptr_ptr = %p ",cnt,cnt_ptr,cnt_ptr_ptr);
    printf("*cnt_ptr = %u, **cnt_ptr_ptr = %u\n",*cnt_ptr,*((uint32_t*)(*((uint64_t*)(cnt_ptr_ptr)))));
}

先不解释,编译运行,结果如下:
在这里插入图片描述
经过我们的代码改造,在没有使用二级指针的前提下,实现了与使用二级指针的同样效果,这就是对复杂规则的拆解,但是也可以看见二级指针对后段代码的简化,拆解是为了理解,正常开发视情况而定,接下来我们利用数据本身和数据解释规则两大法宝来解析这两段代码;

注:前面我们一直提到地址数据本身,但是一直没有说地址本身数据的形式是什么?地址数据是地址空间的编号,那么编号的范围常常能限制机器可以寻找内存空间的范围,32位机器位宽为32位,为了效率,地址寻址肯定是最好一次就找到,那么地址空间的最大编号就是:2^32 = 4GB,对于64位机器,最大地址空间编号为:2^64,这个数可就太大了,一般都没有用到64位,一般48位就足够了,可以寻址:2^48 = 256TB,所以在32位上,地址数据本身是一个32位无符号数,64位上是64位无符号数;

首先,无论几级指针,其数据本身都是一个64位无符号数,不管这个指针是指向一个函数、数组、变量、常量还是执行一个指针变量,其数据本身不变,变的是数据的解释规则;

在第二段代码中:

cnt_ptr_ptr = &cnt_ptr;

cnt_ptr_ptr 是一个64位无符号数,这是它的解释规则,只是恰好它存储的数据是一个存储空间编号而已,难以理解的是下面这段代码:

*((uint32_t*)(*((uint64_t*)(cnt_ptr_ptr))))

按照括号对它进行展开:

前提1uint32_t* cnt_ptr;
推论11、cnt_ptr 本身数据是一个64位无符号数
	  2、这个无符号数被解释为一个32位无符号数所在存储空间的第一个字节的地址空间编号
前提2uint64_t cnt_ptr_ptr;
推论21、cnt_ptr_ptr 本身数据是cnt_ptr变量存储空间第一个字节的地址空间编号,是一个64位无符号数
	  2、cnt_ptr_ptr 被解释为一个64位无符号数
	  
由内到外拆解:
/* 强制把 cnt_ptr_ptr 里面存储的数据解释为一个64位无符号数所在存储空间的第一个字节的地址空间编号
* 结合推论2.1,这就是变量cnt_ptr的首地址
* 注:得到cnt_ptr 的地址
*/
A = (uint64_t*)(cnt_ptr_ptr) 
/*
* 按照A的规则,从变量cnt_ptr的首地址起取 uint64_t 得到的是变量cnt_ptr的本身数据
* 也就是变量cnt存储空间的首地址,其本身数据是一个 uint64_t 
* 注:取出 cnt_ptr 的值
*/
B = *(A)
/*
* 将B中存储的 uint64_t 数据强制解释为一个 uint32_t 变量存储空间的首地址
* 注:得到 cnt 的地址
*/
C = (uint32_t*)(B)
/*
* 按照 uint32_t 的规则从 C 本身数据所代表的空间地址编号处取四个字节
* 注:取出 cnt 的值
*/
D = *(C)

说的很绕,也许水平有限没能讲清楚,哈哈哈!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_37121463/article/details/137206047

智能推荐

稀疏编码的数学基础与理论分析-程序员宅基地

文章浏览阅读290次,点赞8次,收藏10次。1.背景介绍稀疏编码是一种用于处理稀疏数据的编码技术,其主要应用于信息传输、存储和处理等领域。稀疏数据是指数据中大部分元素为零或近似于零的数据,例如文本、图像、音频、视频等。稀疏编码的核心思想是将稀疏数据表示为非零元素和它们对应的位置信息,从而减少存储空间和计算复杂度。稀疏编码的研究起源于1990年代,随着大数据时代的到来,稀疏编码技术的应用范围和影响力不断扩大。目前,稀疏编码已经成为计算...

EasyGBS国标流媒体服务器GB28181国标方案安装使用文档-程序员宅基地

文章浏览阅读217次。EasyGBS - GB28181 国标方案安装使用文档下载安装包下载,正式使用需商业授权, 功能一致在线演示在线API架构图EasySIPCMSSIP 中心信令服务, 单节点, 自带一个 Redis Server, 随 EasySIPCMS 自启动, 不需要手动运行EasySIPSMSSIP 流媒体服务, 根..._easygbs-windows-2.6.0-23042316使用文档

【Web】记录巅峰极客2023 BabyURL题目复现——Jackson原生链_原生jackson 反序列化链子-程序员宅基地

文章浏览阅读1.2k次,点赞27次,收藏7次。2023巅峰极客 BabyURL之前AliyunCTF Bypassit I这题考查了这样一条链子:其实就是Jackson的原生反序列化利用今天复现的这题也是大同小异,一起来整一下。_原生jackson 反序列化链子

一文搞懂SpringCloud,详解干货,做好笔记_spring cloud-程序员宅基地

文章浏览阅读734次,点赞9次,收藏7次。微服务架构简单的说就是将单体应用进一步拆分,拆分成更小的服务,每个服务都是一个可以独立运行的项目。这么多小服务,如何管理他们?(服务治理 注册中心[服务注册 发现 剔除])这么多小服务,他们之间如何通讯?这么多小服务,客户端怎么访问他们?(网关)这么多小服务,一旦出现问题了,应该如何自处理?(容错)这么多小服务,一旦出现问题了,应该如何排错?(链路追踪)对于上面的问题,是任何一个微服务设计者都不能绕过去的,因此大部分的微服务产品都针对每一个问题提供了相应的组件来解决它们。_spring cloud

Js实现图片点击切换与轮播-程序员宅基地

文章浏览阅读5.9k次,点赞6次,收藏20次。Js实现图片点击切换与轮播图片点击切换<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title></title> <script type="text/ja..._点击图片进行轮播图切换

tensorflow-gpu版本安装教程(过程详细)_tensorflow gpu版本安装-程序员宅基地

文章浏览阅读10w+次,点赞245次,收藏1.5k次。在开始安装前,如果你的电脑装过tensorflow,请先把他们卸载干净,包括依赖的包(tensorflow-estimator、tensorboard、tensorflow、keras-applications、keras-preprocessing),不然后续安装了tensorflow-gpu可能会出现找不到cuda的问题。cuda、cudnn。..._tensorflow gpu版本安装

随便推点

物联网时代 权限滥用漏洞的攻击及防御-程序员宅基地

文章浏览阅读243次。0x00 简介权限滥用漏洞一般归类于逻辑问题,是指服务端功能开放过多或权限限制不严格,导致攻击者可以通过直接或间接调用的方式达到攻击效果。随着物联网时代的到来,这种漏洞已经屡见不鲜,各种漏洞组合利用也是千奇百怪、五花八门,这里总结漏洞是为了更好地应对和预防,如有不妥之处还请业内人士多多指教。0x01 背景2014年4月,在比特币飞涨的时代某网站曾经..._使用物联网漏洞的使用者

Visual Odometry and Depth Calculation--Epipolar Geometry--Direct Method--PnP_normalized plane coordinates-程序员宅基地

文章浏览阅读786次。A. Epipolar geometry and triangulationThe epipolar geometry mainly adopts the feature point method, such as SIFT, SURF and ORB, etc. to obtain the feature points corresponding to two frames of images. As shown in Figure 1, let the first image be ​ and th_normalized plane coordinates

开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先抽取关系)_语义角色增强的关系抽取-程序员宅基地

文章浏览阅读708次,点赞2次,收藏3次。开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先关系再实体)一.第二代开放信息抽取系统背景​ 第一代开放信息抽取系统(Open Information Extraction, OIE, learning-based, 自学习, 先抽取实体)通常抽取大量冗余信息,为了消除这些冗余信息,诞生了第二代开放信息抽取系统。二.第二代开放信息抽取系统历史第二代开放信息抽取系统着眼于解决第一代系统的三大问题: 大量非信息性提取(即省略关键信息的提取)、_语义角色增强的关系抽取

10个顶尖响应式HTML5网页_html欢迎页面-程序员宅基地

文章浏览阅读1.1w次,点赞6次,收藏51次。快速完成网页设计,10个顶尖响应式HTML5网页模板助你一臂之力为了寻找一个优质的网页模板,网页设计师和开发者往往可能会花上大半天的时间。不过幸运的是,现在的网页设计师和开发人员已经开始共享HTML5,Bootstrap和CSS3中的免费网页模板资源。鉴于网站模板的灵活性和强大的功能,现在广大设计师和开发者对html5网站的实际需求日益增长。为了造福大众,Mockplus的小伙伴整理了2018年最..._html欢迎页面

计算机二级 考试科目,2018全国计算机等级考试调整,一、二级都增加了考试科目...-程序员宅基地

文章浏览阅读282次。原标题:2018全国计算机等级考试调整,一、二级都增加了考试科目全国计算机等级考试将于9月15-17日举行。在备考的最后冲刺阶段,小编为大家整理了今年新公布的全国计算机等级考试调整方案,希望对备考的小伙伴有所帮助,快随小编往下看吧!从2018年3月开始,全国计算机等级考试实施2018版考试大纲,并按新体系开考各个考试级别。具体调整内容如下:一、考试级别及科目1.一级新增“网络安全素质教育”科目(代..._计算机二级增报科目什么意思

conan简单使用_apt install conan-程序员宅基地

文章浏览阅读240次。conan简单使用。_apt install conan