深入理解指针 (1)
1.内存和地址
1.1内存
1.1.1内存的使用和管理
(1)内存划分为一个个的内存单元,每个内存单元的大小是1个字节,一个内存单元可以存放8个bit。
(2)每个内存单元有一个编号,内存单元的编号在计算机中也叫地址,C语言中称为指针
内存单元的编号 == 地址 == 指针
1.2编址
CPU访问内存中的某个字节空间,必须要知道内存单元的位置(给内存单元编号),地址信息被下达给内存,在内存上就可以找到该地址的对应数据,将数据通过数据总线传入CPU内寄存器
地址总线:传递信息,通过该信息定位内存单元
2.指针变量和地址
2.1变量创建的本质
int a = 20;
//变量创建的本质是在内存中申请空间
//向内存申请4个字节的空间,用来存放20这个数值
//这4个字节每个字节都有编号(地址)
//变量的名字是给程序员看的,编译器是通过地址找内存单元
2.2取地址操作符(&)
int a = 20;//&a, &--取地址操作符,拿到变量a的地址printf("%p\n", &a);int* pa = &a;//pa是一个变量,用来存放地址(指针),pa叫指针变量
理解int* pa = &a;
pa的类型是int*
pa是指针变量的名字
* 表示pa是指针变量
int表示pa指向的变量a的类型是int
2.3解引用操作符(*)
int a = 20;int* pa = &a;*pa = 300;//*--解引用操作符(间接访问操作符)printf("%d\n", a);
2.4指针变量大小
指针变量用来存放地址。
一个32位机器,有32根地址总线,每根地址总线传递一个信号,32根地址总线产生32bit的地址,需要4字节存放。
一个64位机器,有64根地址总线,每根地址总线传递一个信号,64根地址总线产生64bit的地址,需要8字节存放。
指针变量的大小只与机器的位长有关,与本身类型无关。
理解:一个char类型的地址是地址,同时一个int类型的地址同样也是地址,char*, int* 取地址时取的都是地址,所以与类型无关(通俗理解:苹果是水果,香蕉也是水果,当不论是拿苹果还是香蕉时都是拿的水果)
3.指针变量类型的意义
3.1指针的解引用
指针的类型决定了对指针解引用权限有多大。
如:char* 解引用能访问1个字节,int* 解引用能访问4个字节
3.2指针+-整数
指针类型决定了指针向前或向后一步走多远(距离)
如:一个char* 跳过1个字节,一个int* 跳过4个字节
理解:int* pa; pa + 1——> + 1 * sizeof(int)
pa + n——> + n * sizeof(int)
int a = 10;int* pa = &a;char* pb = &a;printf("a = %p\n", &a);printf("pa = %p\n", pa);printf("pb = %p\n", pb);printf("a + 1 = %p\n", &a + 1);printf("pa + 1 = %p\n", pa + 1);printf("pb + 1 = %p\n", pb + 1);
3.3void* 指针
无具体类型的指针(泛型指针),可以用来接收任何类型的地址,但是不能直接进行解引用操作和指针的加减整数操作。即:可接收不同类型地址但不能进行指针运算
void* 一般在函数参数的部分使用,用来接收不同数据类型的地址。使得一个函数能够处理多种类型数据。
4.const修饰指针
4.1const修饰变量
const修饰变量的时候叫:常变量。这个被修改的变量本质上还是变量,只是不能被修改
当使用指针变量可以对其const修饰的变量进行修改(门被关起来了,从窗户跳进去)
const int n = 20;int* p = &n;*p = 200;
4.2const修饰指针变量
4.2.1.const位于*左边
const放在*左边,限制的是指针指向的内容,即,不能通过指针变量来修改它所指向的内容
但是指针变量本身可以被修改
4.2.2.const位于*右边
const放在*右边,限制的是指针变量本身,即,指针不能改变它的指向
但是可以通过指针变量修改它所指向的内容
4.2.3.const位于*左右两侧
此时指针指向的内容和指针变量本身都不能被修改
注:关于指针p有3个相关的值
1.p,p里面存放着一个地址
2.*p,p指向的那个对象
3.&p,表示的是p变量的地址
5.指针运算
5.1指针+-整数
1.指针类型决定了指针+1的步长
2.数组在内存中是连续存放
//顺序打印数组
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(a) / sizeof(a[0]);int* p = &a[0];for (int i = 0; i < sz; i++){printf("%d ", *p);p++;//printf("%d ", *(p + i));}
//逆序打印数组int a[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(a) / sizeof(a[0]);int* p = &a[sz - 1];for (int i = 0; i < sz; i++){//printf("%d ", *p);//p--;printf("%d ", *(p - i));}
5.2指针-指针
指针-指针的绝对值是指针之间元素的个数
指针-指针,计算的前提条件是两个指针指向的是同一个空间
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("%d\n", &a[9] - &a[0]);printf("%d\n", &a[0] - &a[9]);
实例:写一个函数,求字符串长度
1.通过strlen函数求字符串长度
#include <string.h>char ch[] = "abcdef";size_t len = strlen(ch);printf("%zd\n", len);
2.写一个函数,模拟strlen函数实现求字符串长度
方法1:(指针+-整数操作)
size_t my_strlen(char* p)
{int count = 0;while (*p != '\0') //while(*p){count++;p++;}return count;
}int main()
{char ch[] = "abcdef";size_t len = my_strlen(ch); //数组名其实是数组首元素的地址 ch==&ch[0]printf("%zd\n", len);return 0;
}
方法2:(指针-指针操作)
size_t my_strlen(char* p)
{char* start = p;char* end = p;while (*end != '\0') //while(*end){end++;}return end - start;
}int main()
{char ch[] = "abcdef";size_t len = my_strlen(ch); //数组名其实是数组首元素的地址 ch==&ch[0]printf("%zd\n", len);return 0;
}
5.3指针的关系运算
当一个指针指向一个数组时,指针向后移动,指针指向的地址由小变大
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(a) / sizeof(a[0]);int* p = a;//while(p < a + sz)while (p < &a[sz]) //指针的大小比较{printf("%d ", *p);p++;}
6.野指针
指针指向的位置不可知(随机、不正确、无明确限制)
6.1野指针的成因
(1)指针未初始化
一个局部变量的值不初始化,值是随机的
如果将p中存放的值当作地址,解引用操作符会形成非法访问
int* p; //指针未初始化
*p = 10;
(2)指针越界
注:数组下标是从0开始的,不要越界
(3)将指针指向的空间释放
局部变量进函数创建,出函数销毁,当它出函数时,内存空间归还给操作系统
int* test()
{int n = 10;return &n;
}int main()
{int* p = test();printf("%d\n", *p);return 0;
}
6.2如何规避野指针
(1)指针初始化
如果明确知道指向哪里就直接赋值地址,不知道指向哪里赋值NULL,NULL是C语言中的标识符常量,值为0,0也是地址,这个地址无法使用,读写该地址会报错。
int a = 10;
int* p = &a; //直接复制地址
int* s = NULL; //赋值NULL
*s = 100; //error
(2)指针越界
一个程序申请了多少内存空间,指针也只能访问这些空间,超出范围访问就是指针越界。
(3)指针变量不再使用,及时置NULL,使用前检查有效性
只要指针是NULL就不进行访问,指针在使用之前先进行判空再进行解引用操作。
int a = 20;
int* p = &a;
if(*p != NULL) //判空
{*p = 200;
}
(4)避免返回局部变量的地址
不要返回局部变量的地址
7.assert断言
assert.h头文件定义了宏assert(),用于在运行确保程序符合指定条件,如果不符合就会报错终止运行,这个宏叫做断言。
assert()接收一个表达式作为参数,如果表达式为真,不会产生任何作用,程序继续运行,如果表达式为假,assert()会报错,在标准错误流stderr(屏幕上)写入一条错误信息,显示没通过的表达式,以及包含这个表达式的文件名和行号。
优点:(1)自动识别文件和出错的行号
(2)无需更改代码就能开启或关闭assert()机制
(3)如果不需要使用时,在头文件定义宏NDEBUG
#define NDEBUG
#include <assert.h>assert(*p != NULL);
缺点:增加了运行时间
注:在VS的Release版本中会优化掉assert(),即不会起作用
8.指针的调用
8.1传值调用和传址调用
8.1.1传值调用
传值调用就是在函数使用时,把变量本身的值直接传递给函数。实参传递给形参的时候,形参会单独创建一份临时空间来接收实参,此时对形参的修改不会影响对实参的改变。
//传值调用(不能实现变量数值的交换)
void swap1(int x, int y)
{int z = 0;z = x;x = y;y = z;
}int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);printf("交换前:a = %d b = %d\n", a, b);swap1(a, b); //传值调用printf("交换后:a = %d b = %d\n", a, b);return 0;
}
8.1.2传址调用
能建立函数和主调函数之间的联系,可以在函数内部修改主调函数的变量,从而使主调函数中的变量发生改变。
void swap1(int* pa, int* pb)
{int z = 0;z = *pa;*pa = *pb;*pb = z;
}int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);printf("交换前:a = %d b = %d\n", a, b);swap1(&a, &b); //传址调用printf("交换后:a = %d b = %d\n", a, b);return 0;
}
注:如果只是需要主调函数的变量值来实现计算,就可以采用传值调用。如果函数内部要修改主调函数中的变量的值,就需要传址调用。
未完待续……
相关文章:

深入理解指针 (1)
1.内存和地址 1.1内存 1.1.1内存的使用和管理 (1)内存划分为一个个的内存单元,每个内存单元的大小是1个字节,一个内存单元可以存放8个bit。 (2)每个内存单元有一个编号,内存单元的编号在计…...

Leetcode98、230:二叉搜索树——递归学习
什么是二叉搜索树:右子树节点 > 根节点 > 左子树节点, 二叉搜索树中的搜索,返回给定值val所在的树节点 终止条件为传进来的节点为空、或者节点的值 val值,返回这个节点; 单程递归逻辑:定义一个resu…...
4/25 研0学习日志
Python学习 python 4个常用的数据容器 list dict tuple set list 列表中数据类型可以不一样 构造方式 mylist["xxx","xxxx"] 获取数据方式 mylist[1] mylist[:4] mylist[-1:] 添加数据 mylist.append() mylist.extern(["aaa","aaaa&…...

15. LangChain多模态应用开发:融合文本、图像与语音
引言:当AI学会"看听说想" 2025年某智慧医院的多模态问诊系统,通过同时分析患者CT影像、语音描述和电子病历,将误诊率降低42%。本文将基于LangChain多模态框架与Deepseek-R1,手把手构建能理解复合信息的智能系统。 一、…...

2022李宏毅老师机器学习课程笔记
机器学习笔记目录 1.绪论(内容概述)2.机器学习和深度学习的基本概念transformer 1.绪论(内容概述) 机器学习:让机器找一个函数,通过函数输出想要的结果。应用举例:语音识别,图像识别…...

笔试强训:Day2
一、字符串中找出连续最长的数字串(双指针) 字符串中找出连续最长的数字串_牛客题霸_牛客网 #include <iostream> #include <string> #include <cctype> using namespace std;int main() {//双指针string str;cin>>str;int nstr.size();int begin-1,l…...

linux合并命令(一行执行多个命令)的几种方式总结
背景: 最近安装配置机器,需要手打很多命令。又不能使用docker,所以就使用iTerm2连接多台服务器,然后move session到一个窗口中,shift command i使用XSHELL类似的撰写功能,就可以一次在多台服务器命令窗口…...

基于归纳共形预测的大型视觉-语言模型中预测集的**数据驱动校准**
摘要 本研究通过分离共形预测(SCP)框架,解决了大型视觉语言模型(LVLMs)在视觉问答(VQA)任务中幻觉缓解的关键挑战。虽然LVLMs在多模态推理方面表现出色,但它们的输出常常表现出具有…...
【器件专题1——IGBT第2讲】IGBT 基本工作原理:从结构到特性,一文解析 “电力电子心脏” 的核心机制
IGBT(绝缘栅双极型晶体管,Insulated Gate Bipolar Transistor)作为现代电力电子领域的核心器件,其工作原理融合了 MOSFET 的高效控制优势与 BJT 的大功率处理能力。本文从物理结构、导通 / 关断机制、核心特性等维度,深…...
【避坑指南】Spring拦截器中instanceof HandlerMethod失效的问题排查
问题背景 最近在使用Spring MVC开发项目时,我遇到了一个诡异的问题:在自定义拦截器的preHandle方法中,明明请求的是Controller层的方法,但handler instanceof HandlerMethod判断却总是返回false,导致拦截逻辑无法正常…...
青少年编程与数学 02-018 C++数据结构与算法 06课题、树
青少年编程与数学 02-018 C数据结构与算法 06课题、树 一、树(Tree)1. 树的定义2. 树的基本术语3. 常见的树类型4. 树的主要操作5. 树的应用 二、二叉树(Binary Tree)1. 二叉树的定义2. 二叉树的基本术语3. 二叉树的常见类型4. 二叉树的主要操作5. 二叉树的实现代码说明输出示例…...

docker学习笔记5-docker中启动Mysql的最佳实践
一、查找目录文件位置 1、mysql的配置文件路径 /etc/mysql/conf.d 2、mysql的数据目录 /var/lib/mysql 3、环境变量 4、端口 mysql的默认端口3306。 二、启动命令 1、启动命令说明 docker run -d -p 3306:3306 -v /app/myconf:/etc/mysql/conf.d # 挂载配置目录 -v…...

从零开始搭建Django博客③--前端界面实现
本文主要在Ubuntu环境上搭建,为便于研究理解,采用SSH连接在虚拟机里的ubuntu-24.04.2-desktop系统搭建,当涉及一些文件操作部分便于通过桌面化进行理解,通过Nginx代理绑定域名,对外发布。 此为从零开始搭建Django博客…...

系统与网络安全------弹性交换网络(3)
资料整理于网络资料、书本资料、AI,仅供个人学习参考。 STP协议 环路的危害 单点故障 PC之间的互通链路仅仅存在1个 任何一条链路出现问题,PC之间都会无法通信 解决办法 提高网络可靠性 增加冗余/备份链路 增加备份链路后交换网络上产生二层环路 …...

Cursor 配置 MCP Tool
文章目录 1、MCP Tool 的集合2、一个 demo :Sequential Thinking2.1、搜索一个 MCP Tool 获取 command 命令2.2、在 Cursor 配置2.3、配置状态检查与修正(解决网络问题)检查解决办法 2.4、使用 1、MCP Tool 的集合 https://smithery.ai/ 2、一个 demo :Sequential Thinking …...
SQL进阶知识:四、索引优化
今天介绍下关于索引优化的详细介绍,并结合MySQL数据库提供实际例子。 索引优化是数据库性能优化的关键环节之一,尤其是在处理大量数据时。索引可以加快查询速度,减少数据扫描范围,但不当的索引设计也可能导致性能问题。以下是关于…...
【Leetcode 每日一题】2799. 统计完全子数组的数目
问题背景 给你一个由 正 整数组成的数组 n u m s nums nums。 如果数组中的某个子数组满足下述条件,则称之为 完全子数组 : 子数组中 不同 元素的数目等于整个数组不同元素的数目。 返回数组中 完全子数组 的数目。 子数组 是数组中的一个连续非空序…...
OpenCV中的SIFT特征提取
文章目录 引言一、SIFT算法概述二、OpenCV中的SIFT实现2.1 基本使用2.1.1 导入库2.1.2 图片预处理2.1.3 创建SIFT检测器2.1.4 检测关键点并计算描述符2.1.5 检测关键点并计算描述符并对关键点可视化2.1.6 印关键点和描述符的形状信息 2.2 参数调优 三、SIFT的优缺点分析3.1 优点…...

【金仓数据库征文】-《深入探索金仓数据库:从基础到实战》
目录 前言 什么是金仓数据库? 金仓数据库的特点 金仓数据库的核心特点 金仓数据库与其他数据库的对比 金仓数据库的安装 常见的语句 总结 前言 为助力开发者、运维人员及技术爱好者快速掌握这一工具,本文将系统性地介绍金仓数据库的核心知识。内…...

RocketMQ 主题与队列的协同作用解析(既然队列存储在不同的集群中,那要主题有什么用呢?)---管理命令、配置安装
学习之前呢需要会使用linux的基础命令 一.RocketMQ 主题与队列的协同作用解析 在 RocketMQ 中,主题(Topic)与队列(Queue)的协同设计实现了消息系统的逻辑抽象与物理存储分离。虽然队列实际存储在不同集群的 B…...

从岗位依附到能力生态:AI革命下“什么叫就业”的重构与价值
在人工智能(AI)技术深刻重塑社会生产关系的当下,“就业”这一概念正经历着从“职业绑定”到“能力变现”的范式转移。本文将从传统就业观的解构、AI赋能艺术教育的价值逻辑、以及未来就业形态的进化方向三个维度,探讨技术驱动下就业的本质变革,并揭示AI技术如何通过教育创…...
leetcode_二叉树 230. 二叉搜索树中第 K 小的元素
230. 二叉搜索树中第 K 小的元素 给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 小的元素(从 1 开始计数)。 示例 1: 输入:root [3,1,4,null,2], k 1输出:1…...

海外版高端Apple科技汽车共享投资理财系统
这一款PHP海外版高端Apple、科技汽车、共享投资理财系统phplaravel框架。...
架构-软件架构设计
一、软件架构基础概念 1. 软件架构的定义 通俗理解:软件架构是软件系统的“骨架”,定义了系统的结构、行为和属性,就像盖房子的设计图纸,规划了房间布局、承重结构和功能分区。核心作用: 沟通桥梁:让技术…...

企业为何要禁止“片断引用开源软件代码”?一文看透!
开篇故事:一段“开源代码”引发的百亿级灾难 某电商平台为快速上线新功能,从GitHub复制了一段“高性能加密算法”代码到支付系统中。 半年后,黑客通过该代码中的隐藏后门,盗取百万用户信用卡信息。 事后调查:这段代…...
yolo常用操作(长话短说)热力图,特征图,结构图,训练,测试,预测
训练 from ultralytics import YOLOmodel YOLO(ryolo11n.yaml) # 改为模型文件名model.load(yolo11n.pt) # 权重文件名,官网下载results model.train(datarfish.yaml, # 数据yaml文件epochs300,batch8,device0,workers0,workspace4) yaml文件不会搞的࿰…...

【C++指南】告别C字符串陷阱:如何实现封装string?
🌟 各位看官好,我是egoist2023! 🌍 种一棵树最好是十年前,其次是现在! 💬 注意:本章节只详讲string中常用接口及实现,有其他需求查阅文档介绍。 🚀 今天通过了…...

国内ip地址怎么改?详细教程
在中国,更改IP地址需要遵守规则,并确保所有操作合规。在特定情况下,可能需要修改IP地址以满足不同需求或解决特定问题。以下是一些常见且合法的IP地址变更方法及注意事项: 一、理解IP地址 IP地址是设备在网络中的唯一标识&#x…...

模式设计简介
设计模式简介 设计模式是软件开发中经过验证的最佳实践解决方案,它是针对特定问题的通用解决方案,能够帮助开发者提升代码的可维护性、可扩展性和复用性。设计模式并非具体的代码实现,而是一种解决问题的思路和方法论,它源于大量的实践经验总结,旨在解决软件开发过程中反…...

众趣科技X世界读书日丨数字孪生技术赋能图书馆空间智慧化运营
4月23日,是第30个“世界读书日”,不仅是庆祝阅读的日子,更是思考知识传播未来的契机。 图书馆作为主要传播图书的场所,在科技的发展中,图书馆正面临前所未有的挑战,联合国数据显示,全球近30%的…...