指针(一)

news2024/11/16 2:51:14

指针(一)

文章目录

  • 前言
  • 1、内存和地址
    • 1.1.内存
    • 1.2 究竟该如何理解编制
  • 2、指针变量和地址
    • 2.1取地址符操作符(&)
    • 2.2 指针变量和解引⽤操作符(*)
      • 2.2.1 指针变量
      • 2.2.2 如何拆解指针类型
      • 2.2.3 解引⽤操作符
  • 3、指针变量类型的意义
    • 3.1 指针的解引用
    • 3.2 指针+-整数
    • 3.3,void*指针
  • 4、const修饰指针
    • 4.1 const修饰变量


前言

下面要跟大家一起探索C语言最难的部分–指针


正片开始

1、内存和地址

1.1.内存

在计算中,每一个软件都存放在内存中,那么什么是内存呢?
我们知道计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的
数据也会放回内存中,那我们买电脑的时候,电脑上内存是8GB/16GB/32GB等,那这些内存空间如何高效的管理呢?
其实也是把内存划分为一个个的内存单元,每个内存单元的大小取1个字节。
计算机中常见的单位(补充):
⼀个比特位可以存储⼀个2进制的位1或者0

1 bit - ⽐特位
2 byte - 字节
3 KB
4 MB
5 GB
6 TB
7 PB

1byte = 8bit
1KB = 1024byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
1PB = 1024TB

其中,每个内存单元,⼀个字节空间⾥⾯能放8个比特位。
每个内存单元也都有⼀个编号,有了这个内存单元的编号,CPU就可以快速找到⼀个内存空间。
在计算机中我们把内存单元的编号也称为地址。C语言中给地址起了新的名字叫:指针。
所以我们可以理解为:
内存单元的编号 == 地址 == 指针

1.2 究竟该如何理解编制

CPU访问内存中的某个字节空间,必须知道这个
字节空间在内存的什么位置,⽽因为内存中字节很多,所以需要给内存进⾏编址。
计算机中的编址,并不是把每个字节的地址记录
下来,而是通过硬件设计完成的。
钢琴、吉他 上⾯没有写上“剁、来、咪、发、唆、拉、西”这样的信息,但演奏者照样能够准确找到每⼀个琴弦的每⼀个位置,这是为何?因为制造商已经在乐器硬件层⾯上设计好了,并且所有的演奏者都知道。本质是⼀种约定出来的共识!

⾸先,必须理解,计算机内是有很多的硬件单元,⽽硬件单元是要互相协同⼯作的。所谓的协同,⾄少相互之间要能够进⾏数据传递。
但是硬件与硬件之间是互相独⽴的,那么如何通信呢?答案很简单,⽤"线"连起来。
⽽CPU和内存之间也是有⼤量的数据交互的,所以,两者必须也⽤线连起来。
不过,我们今天关⼼⼀组线,叫做地址总线。
硬件编址也是如此我们可以简单理解,32位机器有32根地址总线,
每根线只有两态,表⽰0,1【电脉冲有⽆】,那么⼀根线,就能表⽰2种含义,2根线就能表⽰4种含义,依次类推。32根地址线,就能表⽰2^32种含义,每⼀种含义都代表⼀个地址。
地址信息被下达给内存,在内存上,就可以找到该地址对应的数据,将数据在通过数据总线传⼊CPU内寄存器。

2、指针变量和地址

2.1取地址符操作符(&)

理解了内存和地址的关系,我们再回到C语⾔,在C语⾔中创建变量其实就是向内存申请空间,⽐如:

#include<stdio.h>
int main()
{
	int a = 10;
	return 0;
}

在这里插入图片描述
比如,上述的代码就是创建了整型变量a,内存中申请4个字节,⽤于存放整数10,其中每个字节都有地址,上图中4个字节的地址分别是:

0x006FFD70
0x006FFD71
0x006FFD72
0x006FFD73

那我们如何能得到a的地址呢?
这⾥就得学习⼀个操作符(&)-取地址操作符

#include<stdio.h>
int main()
{
	int a = 10;
	&a;
	printf("%p\n", &a);//%p专门打印地址
	return 0;
}

在这里插入图片描述

在这里插入图片描述
&a取出的是a所占4个字节中地址较⼩的字节的地址。
虽然整型变量占⽤4个字节,我们只要知道了第⼀个字节地址,顺藤摸⽠访问到4个字节的数据也是可⾏的。

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

2.2.1 指针变量

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

#include<stdio.h>
int main()
{
	int a = 20;
	int* pa = &a;

	return 0;
}

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

2.2.2 如何拆解指针类型

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

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

这⾥pa左边写的是 int* , * 是在说明pa是指针变量,⽽前⾯的 int 是在说明pa指向的是整型(int)类型的对象。
那如果有⼀个char类型的变量ch,ch的地址,要放在什么类型的指针变量中呢?

char ch = ‘w’;
pc = &ch;//pc 的类型怎么写呢?

2.2.3 解引⽤操作符

我们将地址保存起来,未来是要使⽤的,那怎么使⽤呢?
在现实⽣活中,我们使⽤地址要找到⼀个房间,在房间⾥可以拿去或者存放物品。
C语⾔中其实也是⼀样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针)指向的对象,这⾥必须学习⼀个操作符叫解引⽤操作符(*)。
在这里插入图片描述上⾯代码中就使⽤了解引⽤操作符, *pa 的意思就是通过pa中存放的地址,找到指向的空间,
pa其实就是a变量了;所以pa = 0,这个操作符是把a改成了0.
有同学肯定在想,这⾥如果⽬的就是把a改成0的话,写成 a = 0; 不就完了,为啥⾮要使⽤指针呢?
其实这⾥是把a的修改交给了pa来操作,这样对a的修改,就多了⼀种的途径,写代码就会更加灵活,后期慢慢就能理解了。

前⾯的内容我们了解到,32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或者0,那我们把32根地址线产⽣的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4个字节才能存储。
如果指针变量是⽤来存放地址的,那么指针变的⼤⼩就得是4个字节的空间才可以。
同理64位机器,假设有64根地址线,⼀个地址就是64个⼆进制位组成的⼆进制序列,存储起来就需要8个字节的空间,指针变量的⼤⼩就是8个字节

#include <stdio.h>
指针变量的⼤⼩取决于地址的⼤⼩
32位平台下地址是32个bit位(即4个字节)
64位平台下地址是64个bit位(即8个字节)
int main()
{
 printf("%zd\n", sizeof(char *));
 printf("%zd\n", sizeof(short *));
 printf("%zd\n", sizeof(int *));
 printf("%zd\n", sizeof(double *));
 return 0;
}

在这里插入图片描述
结论:
• 32位平台下地址是32个bit位,指针变量大小是4个字节
• 64位平台下地址是64个bit位,指针变量大小是8个字节
• 注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,大小都是相同的。

3、指针变量类型的意义

指针变量的⼤⼩和类型⽆关,只要是指针变量,在同⼀个平台下,⼤⼩都是⼀样的,为什么还要有各种各样的指针类型呢?
其实指针类型是有特殊意义的,我们接下来继续学习。

3.1 指针的解引用

对⽐,下⾯2段代码,主要在调试时观察内存的变化。

#include<stdio.h>
int main()
{
	int n = 0x11223344;
	int* pc = &n;
	*pc = 0;
	printf("%xd", n);
	return 0;
}
#include<stdio.h>
int main()
{
	int n = 0x11223344;
	char* pc = &n;
	*pc = 0;
	printf("%xd", n);
	return 0;
}

分别运行一下:
第一个代码调试结果:
在这里插入图片描述

第二个代码调试结果:
在这里插入图片描述
咱们在进行一次内存检测:
第一个代码测试:在这里插入图片描述
第二个代码测试:
在这里插入图片描述调试我们可以看到,代码1会将n的4个字节全部改为0,但是代码2只是将n的第⼀个字节改为0。
结论:指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。
比如: char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节。

3.2 指针±整数

先看⼀段代码,调试观察地址的变化。

#include <stdio.h>
int main()
{
	int n = 10;
	char* pc = (char*)&n;
	int* pi = &n;

	printf("%p\n", &n);
	printf("%p\n", pc);
	printf("%p\n", pc + 1);
	printf("%p\n", pi);
	printf("%p\n", pi + 1);
	return 0;
}

在这里插入图片描述
我们可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。
这就是指针变量的类型差异带来的变化。指针+1,其实跳过1个指针指向的元素。指针可以+1,那也可以-1。
结论:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。

3.3,void*指针

在指针类型中有⼀种特殊的类型是 void * 类型的,可以理解为⽆具体类型的指针(或者叫泛型指针),这种类型的指针可以⽤来接受任意类型地址。但是也有局限性, void* 类型的指针不能直接进行指针的±整数和解引⽤的运算。

#include<stdio.h>
int main()
{
	int a = 10;
	int* pa = &a;
	char* pc = &a;
	return 0;
}

在上⾯的代码中,将⼀个int类型的变量的地址赋值给⼀个char类型的指针变量。编译器给出了⼀个警告(如下图),是因为类型不兼容。⽽使⽤void类型就不会有这样的问题。在这里插入图片描述
使⽤void*类型的指针接收地址:

#include <stdio.h>
int main()
{
	int a = 10;
	void* pa = &a;
	void* pc = &a;

	*pa = 10;
	*pc = 0;
	return 0;
}

调试结果:
在这里插入图片描述
这⾥我们可以看到, void* 类型的指针可以接收不同类型的地址,但是⽆法直接进⾏指针运算。
那么 void* 类型的指针到底有什么⽤呢?
⼀般 void* 类型的指针是使⽤在函数参数的部分,⽤来接收不同类型数据的地址,这样的设计可以实现泛型编程的效果。使得⼀个函数来处理多种类型的数据,在《深⼊理解指针(4)》中我们会讲解。

4、const修饰指针

4.1 const修饰变量

变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。
但是如果我们希望⼀个变量加上⼀些限制,不能被修改,怎么做呢?这就是const的作⽤。

#include <stdio.h>
int main()
{
	int m = 0;
	m = 20;//m是可以修改的
	const int n = 0;
	n = 20;//n是不能被修改的
	return 0;
}

上述代码中n是不能被修改的,其实n本质是变量,只不过被const修饰后,在语法上加了限制,只要我们在代码中对n就⾏修改,就不符合语法规则,就报错,致使没法直接修改n。
但是如果我们绕过n,使⽤n的地址,去修改n就能做到了,虽然这样做是在打破语法规则。

#include <stdio.h>
int main()
{
 const int n = 0;
 printf("n = %d\n", n);
 int*p = &n;
 *p = 20;
 printf("n = %d\n", n);
 return 0;
}

在这里插入图片描述
我们可以看到这⾥⼀个确实修改了,但是我们还是要思考⼀下,为什么n要被const修饰呢?就是为了
不能被修改,如果p拿到n的地址就能修改n,这样就打破了const的限制,这是不合理的,所以应该让
p拿到n的地址也不能修改n,那接下来怎么做呢?

4.2 const修饰指针变量
⼀般来讲const修饰指针变量,可以放在的左边,也可以放在的右边,意义是不⼀样的。

int * p;//没有const修饰?
int const * p;//const 放在*的左边做修饰
int const p;//const 放在的右边做修饰

我们看下⾯代码,来分析具体分析⼀下:

#include <stdio.h>
//代码1 - 测试⽆const修饰的情况
void test1()
{
	int n = 10;
	int m = 20;
	int* p = &n;
	*p = 20;//ok?
	p = &m; //ok?
}
//代码2 - 测试const放在*的左边情况
void test2()
{
	int n = 10;
	int m = 20;
	const int* p = &n;
	*p = 20;//ok?
	p = &m; //ok?
}
//代码3 - 测试const放在*的右边情况
void test3()
{
	int n = 10;
	int m = 20;
	int* const p = &n;
	*p = 20; //ok?
	p = &m; //ok?
}
//代码4 - 测试*的左右两边都有const
void test4()
{
	int n = 10;
	int m = 20;
	int const* const p = &n;
	*p = 20; //ok?
	p = &m; //ok?
}
int main()
{
	//测试⽆const修饰的情况
	test1();
	//测试const放在*的左边情况
	test2();
	//测试const放在*的右边情况
	test3();
	//测试*的左右两边都有const
	test4();
	return 0;
}

结论:const修饰指针变量的时候

const如果放在* 的左边,修
饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。但是指针变量本⾝的内容可变。
const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。


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

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

相关文章

Android Studio编译及调试知识

文章目录 Android Studio编译kotlin项目Android Studio编译Java和kotlin混合项目的过程gradle打印详细错误信息&#xff0c;类似这种工具的使用Android apk 从你的代码到APK打包的过程&#xff0c;APK安装到你的Android手机上的过程&#xff0c;最后安装好的形态&#xff0c;以…

20-Java备忘录模式 ( Memento Pattern )

Java备忘录模式 摘要实现范例 备忘录模式&#xff08;Memento Pattern&#xff09;保存一个对象的某个状态&#xff0c;以便在适当的时候恢复对象 备忘录模式属于行为型模式 摘要 1. 意图 在不破坏封装性的前提下&#xff0c;捕获一个对象的内部状态&#xff0c;并在该对…

Win11 没有网络bug

1.问题描述 没有网络&#xff0c;dns一直是固定的&#xff0c;但是dns已经是自动获取了(MAC地址随机) 2.解决办法 1.首先&#xff0c;删除所有网络的手动dns配置,控制中心那个dns管理没有用,在设置中删除网络,不然问题还会出现 - 2.然后&#xff0c;进入注册表\HKEY_LOCAL_MACH…

第五十三回 入云龙斗法破高廉 黑旋风下井救柴进-AI训练数据处理和读取

罗真人教了公孙胜五雷天罡正法&#xff0c;并让他记住“逢幽而止&#xff0c;遇汴而环”八个字。三人辞别了罗真人&#xff0c;戴宗先回去报信&#xff0c;李逵和公孙胜结伴而行。 走了三天&#xff0c;来到了武冈镇&#xff0c;李逵碰到一个铁匠&#xff0c;叫金钱豹子汤隆&a…

docker学习进阶

一、dockerfile解析 官方文档&#xff1a; Dockerfile reference | Docker Docs 1.1、dockfile是什么&#xff1f; dockerfile是用来构建docker镜像的文本文件&#xff0c;由一条条构建镜像所需的指令和参数构成的脚本。 之前我们介绍过通过具体容器反射构建镜像(docker comm…

【硬件工程师面经整理24_其它】

文章目录 1 功放线性指标调试方法2 功放线性指标之间的关系3 光衰减器的原理4 材料硬度由什么决定&#xff1f;5 晶振市场失效率&#xff1f;6 原码、反码和补码 1 功放线性指标调试方法 调试功放线性指标的方法可以根据具体的情况和要求而有所不同&#xff0c;以下是一般性的…

html--钢琴

代码 <!DOCTYPE html> <html> <head> <meta http-equiv"Content-Type" content"text/html; charsetutf-8" /> <title>html钢琴</title> <script src"js/js.js"></script> <link href"…

vscode setting.json 全局设置 工作区设置 位置 优先级

vscode中setting.json有两种配置权限 一、全局配置&#xff1a;setting.json文件位于C:\Users\Administrator\AppData\Roaming\Code\User\settings.json 二、工作区配置&#xff1a;setting.json文件位于工作区的.vscode\settings.json 当两种配置同时存在时&#xff0c;工作区…

IOS覆盖率报告info文件解读

一&#xff0c;IOS覆盖率报告的生成 在做前端精准测试的时候&#xff0c;对于iOS端&#xff0c;通常会做如下操作&#xff1a; &#xff08;1&#xff09;合并覆盖率数据 如下操作&#xff1a; xcrun llvm-profdata merge coverage_file1657885040728.profraw coverage_fil…

力扣hot100:240.搜索二维矩阵II(脑子)

吉大21级算法分析与设计的一道大题&#xff0c;由于每一行都是排好序的直接逐行二分 可以达到&#xff1a;O(mlogn)。但是这里追求更广的思路可以使用其他方法。 矩阵四分&#xff1a; 在矩阵中用中心点比较&#xff0c;如果target大于中心点的值&#xff0c;则由于升序排列&am…

Java面试(8)

三次握手与四次挥手 三次握手: 客户端与服务端建立TCP连接时总共需要发送三个包 三次握手过程中容易引发SYN(DDOS)攻击,所谓SYN攻击是指: 攻击客户端,在短时间内伪造大量不存在的IP地址,向服务端不断发送syn包,服务端收到包后向客户端发送确认包,但由于客户端IP不存在,导致服务…

FPGA FIFO 读取模式

FPGA FIFO 读取模式分两种&#xff1a; Normal Mode: In normal mode, the “rdreq” signal serves as the read request or read enable. When this signal goes high, the data output provides the first data from the FIFO.Essentially, in normal mode, data is availa…

Prompt进阶系列1:LangGPT(从编程语言反思LLM的结构化可复用提示设计框架)

Prompt进阶系列1:LangGPT(从编程语言反思LLM的结构化可复用提示设计框架) 大语言模型 (Large Language Models, LLMs) 在不同领域都表现出了优异的性能。然而&#xff0c;对于非AI专家来说&#xff0c;制定高质量的提示来引导 LLMs 是目前AI应用领域的一项重要挑战。现有的提示…

【经典案例】某大型公园构建检查监督机制项目纪实

——引入网格化监督管理机制&#xff0c;实现责任、人员、信息三位一体 公园管理由于其本身地域范围广的特性在工作中很难进行有效的监督检查&#xff0c;该公园的监督检查由不同的部门分别负责&#xff0c;同部门检查时往往会处于情面而使检查流于形式&#xff0c;并且公园的监…

VSCode搭建ARM开发环境

为了构建Cortex M系列单片机免费开源的开发环境&#xff0c;网络上了解来看VSCODEGCCJLINK是一套比较高效的组合方式&#xff0c;下面记录环境搭建的流程。 我这边的PC环境为 WIN7专业版64bit。 需要用到的工具 Visual Studio CodeSTM32CubemxARM GCC 交叉编译工具链&#x…

javaWebssh文玩竞价管理系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 java ssh文玩竞价管理系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0…

MySQL--索引底层数据结构详解

索引是什么&#xff1f; 索引是帮助MySQL高效获取数据的排好序的数据结构&#xff0c;因此可知索引是数据结构。 概念很抽象&#xff0c;但是类比生活中的例子就很容易理解&#xff0c;比如一本厚厚的书&#xff0c;我们想取找某一小节&#xff0c;我们可以根据目录去快速找到…

复试人工智能前沿概念总结

1.大模型相关概念&#xff08;了解即可&#xff09; 1.1 GPT GPT&#xff0c;全称为Generative Pre-training Transformer&#xff0c;是OpenAI开发的一种基于Transformer的大规模自然语言生成模型。GPT模型采用了自监督学习的方式&#xff0c;首先在大量的无标签文本数据上进…

Linux配置.bashrc文件导致各种命令(vim、sudo)失效。

Linux配置.bashrc文件导致各种命令&#xff08;vim、sudo&#xff09;失效。 起因是 nvcc-V一直报错&#xff1a;-bash&#xff1a;nvcc&#xff1a; command not found 踩坑记录&#xff1a;上网一查说是没有配置cuda的环境变量。于是去修改了bashrc文件&#xff0c;在最下面…

Imagination:RISC-V CPU的重要力量

根据SHD集团最近发布的报告显示&#xff0c;RISC-V正全速发展中。通过分析从2021年到2030年这十年间RISC-V核在不同应用和功能领域的潜在市场&#xff0c;作者Rich Wawrzyniak得出结论称&#xff0c;到2030年&#xff0c;22.3%的SoC将包含RISC-V CPU&#xff0c;RISC-V的收入预…