【C语言】解开指针|编址内存解引用指针产量|

news2024/10/5 17:26:57
  🌈write in front :

🔍个人主页 : 😊@啊森要自信的主页

✨ 作者寄语 🌈: 小菜鸟的力量不在于它的体型,而在于它内心的勇气和无限的潜能,只要你有决心,就没有什么事情是不可能的。

欢迎大家关注🔍点赞👍收藏⭐️留言📝>希望看完我的文章对你有小小的帮助,如有错误,可以指出,让我们一起探讨学习交流,一起加油鸭。 请添加图片描述

文章目录

  • 开端
  • 一、内存和地址
    • 1.1 内存
    • 1.2 怎么理解编址呢?
  • 二、 指针变量和地址
    • 2.1 取地址操作符(&)
    • 2.2指针变量和解引⽤操作符(*)
      • 2.2.1 如何拆解指针类型
      • 2.2.2 解引⽤操作符
    • 2.3 指针变量的⼤⼩
  • 三、 指针变量类型的意义
    • 3.1 指针的解引⽤
    • 3.2 指针+ - 整数
    • 3.3 void* 指针


开端

C语言中的指针😃是一种特殊的变量,它存储了一个内存地址,该地址指向另一个变量的位置。指针允许程序直接访问和操作内存中的数据,而不需要将数据复制到另一个位置

指针在C语言中具有重要的作用,它可以用于动态内存分配、数组和字符串操作、函数传递参数等方面。通过指针,程序可以更灵活地处理内存中的数据,提高了程序的效率和性能。

看到这里,你可能会想到指针竟然有这么多的用处,但是我都不会呀?接下来博主带你一起解开指针的面纱,体会不一样的指针!


一、内存和地址

1.1 内存

在学习内存和地址之前,我们想想这个:
请添加图片描述

当我们早八要去教室上课时,我们冲到教学楼,我们怎么找到我们上课的教室呢?像教室有这么多个,我们不可能一个一个的找,不然这样很容易迟到,同样效率也就低了,但是如果我们给每个教室门口上个牌子,然后编上号:一栋教学楼—>

  1. 一楼:101,102,103…
  2. 二楼:201,202,203…

  3. 在这里插入图片描述

有了这个教室门牌号,你就可以立马跑上去,快速找到,并签到,不会迟到。

接下来,我们把上面的案例对比我们的计算中,又会怎么样呢?

CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中,买电脑的,电脑上的内存8G/16G/32G等,这些内存空间是怎么高效的管理我们的数据的呢?
在这里插入图片描述

在内存中,内存划分为一个一个内存单元,每个内存单元的大小取1个字节一个字节有多大呢?
计算机中常⻅的单位(补充):
⼀个⽐特位(bit)可以存储⼀个2进制的位1或者0

bit - ⽐特位
byte - 字节
KB - 千字节
MB -兆字节
GB - 千兆字节
TB - 千千兆字节
PB - 拍字节
  1. 1byte = 8bit
  2. 1KB = 1024byte
  3. 1MB = 1024KB
  4. 1GB = 1024MB
  5. 1TB = 1024GB
  6. 1PB = 1024TB

每个内存单元也都有⼀个编号(这个编号就相当于教室的⻔牌号),有了这个内存单元的编号,CPU就可以快速找到这个内存空间。

生活中我们把门牌号也叫地址,在计算机中我们把内存单元的编号也称为地址。C语⾔中给地址起了新的名字叫:指针。

所以我们可以理解为:
内存单元的编号 == 地址 == 指针
在这里插入图片描述

1.2 怎么理解编址呢?

CPU访问内存中的某个字节空间,必须知道这个
字节空间在内存的什么位置,而因为内存中字节
很多,所以需要给内存进行编址(就如同教室很
多,需要给教室编号一样)。
在这里插入图片描述
小知识来了:计算机中的编址,并不是把每个字节的地址记录下来,⽽是通过硬件设计完成的。也就是说计算中的编址,不是把整个地址编号记录下来,而是制造商已经在硬件层⾯上设计好了。

计算机内部有许多硬件单元,这些单元需要相互协作。协作的意思是它们至少要能够进行数据传输。但是,硬件单元之间是相互独立的,那么它们如何进行通信呢?

答案很简单,通过连接线进行通信。

CPU想读取内存中某一个数据中时,控制总线发出一个信号,CPU通过地址总线把内存中的这个位置找到,然后再通过数据总线传给CPU.

CPU和内存之间也需要进行大量的数据交互,因此它们必须通过连接线进行连接。然而,今天我们要关注的是一组连接线,称为地址总线。

学到这里,同学们可能有个疑问,地址总线有多少根,怎么构建联系的呢?32位机器有32根地址总线,64位机器有64根地址总线,每根线只有两态,表⽰0,1【电脉冲有⽆】,那么⼀根线,就能表⽰2种含义,2根线就能表⽰4种含义,依次类推。32根地址线,就能表⽰2^32种含义,每⼀种含义都代表⼀个地址。地址信息被下达给内存,在内存上,就可以找到该地址对应的数据,将数据在通过数据总线传⼊CPU内寄存器。
在这里插入图片描述

二、 指针变量和地址

2.1 取地址操作符(&)

当我们理解了内存和地址的关系,我们再回到C语⾔,在C语⾔中创建变量其实有两种含义:

#include <stdio.h>
int main()
{
 	int a = 66;内层含义
 	return 0;
}

&a取出的是类型为a所占4个字节中地址较小的地址。我们知道a的地址,就能推出他剩下的三个地址了,就可以访问到他4个字节的数据。
在这里插入图片描述
16进制转换为2进制:
在这里插入图片描述
怎么观测到a的地址呢?
按F10调试起来,打开窗口找到内存窗口,4个点击哪一个都可以。
在这里插入图片描述

输入&取地址操作符(&a)就可以找到对应的地址,当然也可以在监视看,以下在内存中观察:
在这里插入图片描述

2.2指针变量和解引⽤操作符(*)

那我们通过取地址操作符(&)拿到的地址是⼀个数值,⽐如:0x006ffae0,这个数值有时候也是需要存储起来,⽅便后期再使⽤的,那我们把这样的地址值存放在哪⾥呢?答案是:指针变量中。
⽐如:

#include <stdio.h>
int main()
{
 	int a = 10;
 	int* pa = &a;//取出a的地址并存储到指针变量pa中
 	return 0
}

请添加图片描述

指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址。

2.2.1 如何拆解指针类型

我们看到pa的类型是 int* ,我们该如何理解指针的类型呢?

nt a = 10;
int * pa = &a;

这⾥pa左边写的是 int** 是在说明pa是指针变量,⽽前⾯的 int 是在说明pa指向的是整型(int)类型的对象。

在这里插入图片描述

在这里插入图片描述

那如果有⼀个char类型的变量chch的地址,要放在什么类型的指针变量中呢?

char ch = 'R';
pc = &ch;//pc 的类型怎么写呢?

答案是:
char *pc = &ch;

2.2.2 解引⽤操作符

解引用运算符( * ) 将指针变量所指向的对象的值赋给左值变量。当使用指针变量时,使用解引用运算符来访问指针变量所指向的对象。

#include <stdio.h>
int main()
{
	int a = 100;
	int* pa = &a;
	*pa = 0;
	printf("%d", a);
	return 0;
}

*pa 的意思就是通过pa中存放的地址,找到指向的空间,
*pa其实就是a变量了;所以*pa = 0,这个操作符是把a改成了0
在这里插入图片描述

#include <stdio.h>

int main()
{
    int num1 = 10;
    int *ptr1 = &num1;

    printf("The value of num1 is %d\n", num1);
    printf("The address of num1 is %d\n", &num1);
    printf("The value of ptr1 is %d\n", ptr1);
    printf("The value of *ptr1 is %d\n", *ptr1);

    return 0;
}

变量 num1 的值为 10,因为它被赋值为 10。
变量 num1 的地址为 13629256,因为它在内存中的位置是 13629256。
变量 ptr1 的值为 13629256,因为它被赋值为 num1 的地址。
变量 *ptr1 的值为 10,因为它是 ptr1 所指向的对象的值。
在这里插入图片描述

2.3 指针变量的⼤⼩

指针变量的⼤⼩取决于地址的⼤⼩

• 32位平台下地址是32个bit位,指针变量⼤⼩是4个字节
• 64位平台下地址是64个bit位,指针变量⼤⼩是8个字节
• 注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。

32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或者0,那我们把32根地址线产⽣的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4个字节才能存储。

#include <stdio.h>
int main()
{
	int* a = 10;
	short* b = 10;
	char* c = 10;
	double* d = 10;
	
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(b));
	printf("%d\n", sizeof(c));
	printf("%d\n", sizeof(d));
	return 0;
}

debug X86也就是调试状态下的32位环境下:
在这里插入图片描述

debug X64也就是调试状态下的64位环境下:
在这里插入图片描述

三、 指针变量类型的意义

当你看到这里,你发现指针变量的⼤⼩和类型⽆关,只要是指针变量,在同⼀个平台下,⼤⼩都是⼀样的, 那还要那么多的指针类型干嘛呢?统一归为一种指针类型不就好了,就没那么麻烦了,bug或许可能就没有那么多了!同学带着你的疑问,让我们一起走下去。看看为什么这么设计的?

3.1 指针的解引⽤

首先,我们观察两组代码的变化环境为debug x86

  1. 代码1:
#include <stdio.h>
int main()
{
	int n = 0x66778899;
	int* pk = &n;
	*pk = 0;
	return 0;
}

请添加图片描述

  1. 代码2:
#include <stdio.h>
int main()
{
	int n = 0x66778899;
	char* pk = (char*)&n;
	*pk = 0;
	return 0;
}

请添加图片描述
调试我们可以看到,代码1会将n的4个字节全部改为0,但是代码2只是将n的第⼀个字节改为0。

结论:指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。

⽐如: char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节

3.2 指针+ - 整数

指针加减整数的语法如下:

ptr + n
ptr - n

其中,ptr 是指针变量,n 是整数。

当我们向一个指针加减整数时,我们实际上是在向指针所指向的内存地址加减整数。这意味着,如果我们向一个指针加 1,则指针会指向内存中下一个字节的位置。如果我们向一个指针减 1,则指针会指向内存中上一个字节的位置。

以下是不同类型的指针加减整数的示例:

整数指针:

int *p = 0;
p++; // 指向内存中下一个字节的位置
p--; // 指向内存中上一个字节的位置

指针数组:

int arr[] = {1, 2, 3, 4, 5};
int *p = arr;//int*类型加4
p++; // 指向内存中下一个元素的位置
p--; // 指向内存中上一个元素的位置

以下是指针加减 1 的示例:

#include <stdio.h>
int main()
{
	int n = 10;
	char* pc = (char*)&n;
	int* pi = &n;
	int* arr[] = { 1,2,3,4,5 };
	int* pk = arr;
	printf("%p\n", &n);
	printf("%p\n", pc);
	printf("%p\n", pc + 1);
	printf("%p\n", pi);
	printf("%p\n", pi + 1);
	printf("%p\n", pk);
	printf("%p\n", pk + 1);
	return 0;
}

运行结果:

可以看到,
char类型的指针变量+1跳过1个字节,int类型的指针变量+1跳过了4个字节。这表明指针变量的类型差异会导致步长的变化。
结论:指针的类型决定了指针向前或向后移动一步的距离。

3.3 void* 指针

void 指针是 C 语言中一种特殊的指针,它可以指向任何类型的数据。void 指针的类型是 void,它不指向任何特定的数据类型。

void* 类型的指针不能直接进⾏指针的±整数和解引⽤的运算。

void 指针也可以用来解引用,但必须在解引用之前使用类型转换。void` 指针可以用来存储指向任何类型数据的指针。

#include <stdio.h>
int main()
{
	int a = 88;
	void* p=&a;
	int* q = (int*)p;//类型转换
	printf("n = %d\n", *q);
	return 0;
}

在这里插入图片描述

void* 类型的指针不能直接进⾏指针的±整数和解引⽤的运算。
在这里插入图片描述


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

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

相关文章

Linux操作系统使用及C高级编程-D3Linux shell命令(权限、输入输出)

Shell 是一种应用程序&#xff0c;用以完成用户与内核之间的交互 一个功能强大的编程语言&#xff08;C语言&#xff09; 一个解释执行的脚本语言&#xff0c;不需要编译&#xff0c;写完直接执行 目前Linux 乌班图的Shell默认是bash 查看当前提供的Shell&#xff1a;cat /…

Vue.js中的路由(router)和Vue Router的作用?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

2.8 CE修改器:寻找共享代码

本关我们将学习共享代码&#xff0c;在C语言中角色属性都是以结构体的方式进行存储的&#xff0c;而结构体所存储的信息都是连续性的&#xff0c;这一关我们将会解释如何处理游戏中的共用代码&#xff0c;这种代码是通用在除了自己以外的其他同类型对像上的常常你在修改游戏的时…

在Vue.js中,什么是mixins?它们的作用是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

Linux基础环境开发工具的使用(三):gdb调试器

Linux基础环境开发工具的使用[三]:gdb调试器 一.调试命令的应用场景1.为什么要介绍调试命令的应用场景呢?2.调试命令的应用场景1.找到问题1.不借助调试2.调试 2.解决问题1.不借助调试2.借助调试 二.调试命令1.gdb使用的前置说明2.基础指令3.断点相关指令4.范围查找相关操作5.局…

通信原理板块——线性分组码之循环码

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 1、循环码原理 循环码(cycle code)…

C# Onnx LSTR 基于Transformer的端到端实时车道线检测

目录 效果 模型信息 项目 代码 下载 效果 模型信息 lstr_360x640.onnx Inputs ------------------------- name&#xff1a;input_rgb tensor&#xff1a;Float[1, 3, 360, 640] name&#xff1a;input_mask tensor&#xff1a;Float[1, 1, 360, 640] -----------------…

罕见!阿里云全系产品崩了。。

作者&#xff1a;苍何&#xff0c;前大厂高级 Java 工程师&#xff0c;阿里云专家博主&#xff0c;CSDN 2023 年 实力新星&#xff0c;土木转码&#xff0c;现任部门技术 leader&#xff0c;专注于互联网技术分享&#xff0c;职场经验分享。 &#x1f525;热门文章推荐&#xf…

安全认证框架Shrio学习,入门到深度学习,SpringBoot整合Shiro小案例,含代码

权限概述 什么是权限 什么是权限 权限管理&#xff0c;一般指根据系统设置的安全策略或者安全规则&#xff0c;用户可以访问而且只能访问自己被授权的资源&#xff0c;不多不少。权限管理几乎出现在任何系统里面&#xff0c;只要有用户和密码的系统。 权限管理再系统中一般分…

两个序列(数论)

两个序列 Problem:B Time Limit:1000ms Memory Limit:65535K Description Gugu 有两个长度无限长的序列A,BA0a^0/0!,A1a^1/1!,A2a^2/2!,A3a^3/3!…. B00, B1b^1/1!,B20,B3b^3/3!,B40, B5b^5/5! … Douge 看到这道这两个序列很觉得很麻烦&#xff0c;所以他想到一个好点子&…

【LeetCode刷题-二分查找】--704.二分查找

704.二分查找 class Solution {public int search(int[] nums, int target) {if(nums.length 0){return -1;}int left 0,right nums.length - 1;while(left < right){int mid (right - left) / 2 left;if(nums[mid] target){return mid;}else if(nums[mid] < targe…

模拟信号数字化--低通抽样与带通抽样

模拟信号数字化 本文主要涉及模拟信号数字化的基本概念&#xff0c;以及对低通抽样与带通抽样以及其公式推导的详细介绍。关于通信原理还有其他文章可参考&#xff1a; 1、信息量、码元、比特、码元速率、信息速率详细解析——实例分析 2、模拟系统的AM信号的调制与频域分析 3、…

HCIE-Rainbow迁移工具

Rainbow迁移工具 Rainbow迁移工具支持p2v&#xff08;物理机到虚拟机的迁移&#xff09; v2v&#xff08;虚拟机到虚拟机的迁移&#xff09; Rainbow业务上云迁移&#xff1a; Rainbow迁移到公有云&#xff08;利用公有云SMS服务&#xff0c;付费&#xff09; Rainbow迁移到公…

【python】sys-path和模块搜索路径

我们在导入一个模块的时候&#xff0c;比如说&#xff1a; import math它必然是有搜索路径的&#xff0c;那到底是在哪个目录下面找呢&#xff1f;Python解释器去哪里找这个文件呢&#xff1f;只有找到这个文件才能读取、装载运行该模块文件。 它一般按照如下路径寻找模块文件…

matlab simulink PSO算法优化simulink的PID参数

1、内容简介 略 13-可以交流、咨询、答疑 PSO算法优化simulink的PID参数 2、内容说明 标准的PSO算法优化simulink的PID参数 PSO、粒子群算法、simulink参数优化 3、仿真分析 4、参考论文 略 链接&#xff1a;https://pan.baidu.com/s/1yQ1yDfk-_Qnq7tGpa23L7g 提取码&…

企业知识库建设指南:实用经验分享

企业知识库建设是提升内部协作和客户支持效率的重要举措。一个完善的知识库可以帮助企业集中管理和传播知识&#xff0c;提供便捷的自助服务和丰富的编辑工具&#xff0c;从而提升用户体验和品牌好感度。接下来就分享一些经验&#xff0c;关于该如何构建一个高效的企业知识库。…

27 _ 递归树:如何借助树来求解递归算法的时间复杂度?

我们都知道,递归代码的时间复杂度分析起来很麻烦。有一个巧妙的方式是借助递归树来分析递归算法的时间复杂度。 递归树与时间复杂度分析 我们前面讲过,递归的思想就是,将大问题分解为小问题来求解,然后再将小问题分解为小小问题。这样一层一层地分解,直到问题的数据规模…

不可思议,才一周,阅读量就突破千万了

这段时间&#xff0c;我发布的文章阅读量看上去还算可以&#xff0c;但我知道&#xff0c;这并不是终点&#xff0c;而是起点。我深知&#xff0c;写作的道路永无止境&#xff0c;只有不断努力&#xff0c;才能在文字的海洋中游得更远。 在这条道路上&#xff0c;我始终坚持用心…

将VS工程转为Qt的pro工程及VS安装Qt插件后没有create basic .pro file菜单问题解决

目录 1. 前言 2. VS工程转为pro工程 3. 没有create basic .pro file菜单 1. 前言 很多小伙伴包括本人&#xff0c;如果是在Windows下开发Qt程序&#xff0c;偏好用Visual Studio外加装个Qt插件进行Qt开发&#xff0c;毕竟Visual Studio确实是功能强大的IDE&#xff0c;但有时…

牛客刷题记录11.12

继承和组合 二进制数统计 1的个数 和 0 的个数