C语言指针详解(1)(能看懂字就能明白系列)文章超长,慢慢品尝
目录
1、内存和地址
2、指针简介
与指针相关的运算符:
取地址操作符(&)
解引用操作符(间接操作符)(*)
编辑 指针变量的声明
指针变量类型的意义
指针的基本操作
1、指针与整数相加(当然也可以相减):
2、指针与指针相加(或者相减)
3、比较
野指针
1、解引用未初始化的指针
2、指针越界访问
3、指针指向的空间释放
4、如何规避野指针
1、指针初始化
2、小心指针越界
1、内存和地址
什么是内存呢?我们知道计算机上CPU(中央处理器)在处理数据的时候,需要的是数据是在内存中读取的,处理后的数据也会放回内存中,比如电脑上的内存是8GB/16GB/32GB等,那这些内存空间是如何高效的管理的呢?
其实也是把内存划分为一个个的内存单元,每个内存单元的大小取一个字节。
补充:计算机中常见的单位——一个比特位可以存储一个二进制的位1或者0
1byte = 8bit(bit-比特位,byte-字节)
1KB = 1024byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
1PB = 1024TB
其实,每个内存单元,相当于一个学生宿舍,一个字节空间里面能放8个比特位,就好比一个宿舍里面住了八个人,每个人就是一个比特位。
每个内存单元也都有一个编号(这个编号就相当于宿舍房间的门牌号),有了这个内存单元的编号,CPU就可以快速找到一个内存空间。生活中我们把门牌号也叫地址,在计算机中我们把内存单元的编号也称为地址。C语言中给地址起了新的名字:指针。
所以我们可以理解为:
内存单元的编号==地址==指针
2、指针简介
指针?什么是指针?从根本上,指针(pointer)是一个值为内存地址的变量(或者数据对象)。比如char类型变量的值是字符,int类型变量的值是整数,指针变量的值是地址。
与指针相关的运算符:
那我们如何获得地址呢?
取地址操作符(&)
一般注解:后跟一个变量名,&给出该变量的地址
#include<stdio.h>
int main()
{int a = 10;&a;//这里是取出a的地址的意思printf("%p", &a);//p是打印地址的占位符return 0;
}
如图所示,0133F70C就是a的地址,(特别注意的是,上述代码在创建变量a的时候,会向内存申请4个字节的空间,用于存放整数10,其中每个字节都有地址,且&a取出的是a所占4个字节中地址较小的字节的地址。)虽然整型变量占4个字节,但我们只要知道了第一个字节地址,就能顺藤摸瓜访问到4个字节的数据。
我们把取到的数据(地址)放到指针变量存储,指针变量也是一种变量,这种变量就是用来存放地址的,存放在指针变量中的值都会理解为地址。
解引用操作符(间接操作符)(*)
一般注解:后跟一个指针名或者地址时,*给出储存在指针指向地址上的值。
示例:
int a=10;
int *pa=&a;//这里pa左边写的是int*,*是在说明pa是指针变量,而前面的int是在说明pa指向的是整型(int)类型的对象。
*pa=20;//*pa等价于a,最终a的值等于20
指针变量的声明
声明指针变量时必须指定指针所指向变量的类型,因为不同的变量类型占用不同的存储空间,一些指针操作要求知道操作对象的大小。另外,程序必须知道存储在指定地址上的数据类型。long和float可能占用相同的存储空间,但是它们存储数字却大相径庭。下面是一些示例:
int *pa //pa是指向int类型变量的指针
char *pb //pb是指向char类型变量的指针
float *pc //pc是指向float类型变量的指针
类型说明符表示了指针所指向对象的类型,星号(*)表明声明的变量是一个指针。
int*pa声明的意思是pa是一个指针,*pa是int类型。
指针变量类型的意义
指针变量的大小和类型无关,只要是指针变量,在同一个平台上,大小都是一样的,为什么还要各种各样的指针类型呢?
其实指针类型是有特殊意义的,例如以下代码:
#include <stdio.h>
int main()
{int n = 0x11223344;int* pi = &n;*pi = 0;printf("%p", *pi);return 0;
}
#include <stdio.h>
int main()
{int n = 0x11223344;char* pc = (char*)&n;*pc = 0;printf("%p", n);return 0;
}
从上面两个代码可以看出,第一个代码会将n的四个字节全部改为0,但是第二个代码只是将n的第一个字节改为0。
结论:指针的类型决定了,对指针解引用的时候有多大权限(一次能操作几个字节)(指针的类型决定了指针向前或者向后走一步有多大)
比如:char*的指针解引用就只能访问一个字节,而int*类型的解引用就能访问四个字节。
指针的基本操作
1、指针与整数相加(当然也可以相减):
可以使用+运算符把指针和整数相加,或整数与指针相加。无论哪种情况,整数都会和指针所指向类型的大小(以字节为单位)相乘,然后把结果与初始地址相加。
#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跳过一个字节,int*类型的指针变量+1跳过了4个字节。这就是指针变量的类型差异带来的变化。
另外再举一个数组的例子:
我们知道数组在内存中都是连续存放的,所以只要我们知道第一个元素的地址,就能顺藤摸瓜找到后面的所以元素。
#include<stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);int* pst = &arr[0];for (int i = 0;i < sz;i++){printf("%d ", *(pst + i));//这里的p+i就是指针加整数}return 0;
}
2、指针与指针相加(或者相减)
int my_strlen(char* s)//定义一个求字符串长度的函数
{char* p = s;while (*p != '\0')p++;return p-s;//这里就是指针减指针
}#include<stdio.h>
int main()
{printf("%d", my_strlen("abc"));//传参后打印结果return 0;
}
3、比较
可以使用关系运算符比较两个指针的值,前提是两个指针都指向相同类型的对象。
#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = &arr[0];int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);while (p < arr + sz) //这里是指针大小的比较{printf("%d ", *p);p++;}return 0;
}
野指针
成因:
1、解引用未初始化的指针
int* pst; //未初始化的指针
*pst=5; //严重的错误
为何不行呢,第二行的意思是把5存储在pst指向的位置。但是pst未被初始化未被初始化,其值是一个随机值,所以不知道将5存储在何处,这可能不会出什么错,也可能会擦写数据或代码,或者导致程序崩溃。
切记:创建一个指针时,系统只分配了存储指针本身的内存,并未分配存储数据的内存。因此,在使用指针之前,必须先用已分配的地址初始化它。
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
2、指针越界访问
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = &arr[0];
int i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
3、指针指向的空间释放
#include <stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("%d\n", *p);
return 0;
}
4、如何规避野指针
1、指针初始化
如果明确知道指针指向哪里就直接赋值地址,如果不知道指针应该指向哪里,可以给指针赋值NULL,NULL是C语言中定义的一个标识符常量,值是0,0也是地址,这个地址是无法使用的,读写该地址会报错。
#include <stdio.h>
int main()
{
int num = 10;
int*p1 = #
int*p2 = NULL;
return 0;
}
2、小心指针越界
一个程序向内存申请了哪些空间,,通过指针也就只能访问哪些空间,不能超过访问范围,超过了就是越界访问。
今天的笔记到此结束了
相关文章:

C语言指针详解(1)(能看懂字就能明白系列)文章超长,慢慢品尝
目录 1、内存和地址 2、指针简介 与指针相关的运算符: 取地址操作符(&) 解引用操作符(间接操作符)(*) 编辑 指针变量的声明 指针变量类型的意义 指针的基本操作 1、指针与整数相加…...

为什么别人年薪30W+?同样为测试人,“我“的测试之路...
目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 1、软件测试员&am…...

【Unity】XML文件的解析和生成
目录 使用XPath路径语法解析 使用xml语法解析 XML文件的生成 XML文件是一种常用的数据交换格式,它以文本形式存储数据,并使用标签来描述数据。解析和生成XML文件是软件开发中常见的任务。 解析XML文件是指从XML文件中读取数据的过程。在.NET中&#…...

Vue h5页面手指滑动图片
场景: 四张图,要求随着手指滑动而滑动 代码: imgs是父盒子 poster-item是每个图片 .imgs {white-space: nowrap;overflow: hidden;overflow-x: auto;margin-bottom: 17px;.poster-item {display: inline-block;vertical-align: middle;wid…...
Python类属性下划线的意义
在Python中,类属性(class attribute)前面带有下划线的命名约定有一些特殊的含义,但它并不会影响属性的实际行为。这是一种命名约定,用于指示属性的用途和访问级别。以下是一些常见的下划线命名约定: 1. 单…...
DbUtils概述
概述 JDBC实用工具组件 Commons DbUtils库是一个小的类集,旨在使使用JDBC更容易。JDBC资源清理代码是平凡的,容易出错的工作,所以这些类从代码中抽象出所有的清理任务,留给你真正想用JDBC做的事情:查询和更新数据。 …...

大数据基础设施搭建 - Hadoop
文章目录 一、下载安装包二、上传压缩包三、解压压缩包四、配置环境变量五、测试Hadoop5.1 测试hadoop命令5.2 测试wordcount案例5.2.1 创建wordcount输入文本信息5.2.2 执行程序5.2.3 查看结果 六、分发压缩包到集群中其他机器6.1 分发压缩包6.2 解压压缩包6.3 配置环境变量 七…...

测试开发环境下centos7.9下安装docker的minio
按照以下方法进行 1、安装docker,要是生产等还是要按照docker-ce yum install docker 2、启动docker service docker start 3、 查看docker信息 docker info 4、加到启动里 systemctl enable docker.service 5、开始docker pull minio/minio 但报错&#x…...
Django之模版层
目录 一、常用语法 二、模版语法之变量 三、模板之过滤器(Filters) 【1】default 【2】length 【3】filesizeformat 【4】slice 【5】date 【6】safe 【7】truncatechars 【8】其它过滤器(了解) 四、模版之标签 【1】for标签 【2】if 标签…...

spark性能调优 | 内存优化
目录 我们先了解一下有哪些内存温馨提示RDD示范(spark版本2.1.1)RDD进行优化Df和Ds进行示范 我们先了解一下有哪些内存 1.storage内存 存储数据,缓存 可预估2.shuffle内存 计算join groupby 不可预估spark1.6之前 静态管理的,spark1.6之…...
【PG】PostgreSQL高可用之自动故障转移-repmgrd
前言 上面的几篇文章介绍了repmgr的部署,手动进行 从节点提升,主从切换,孤立从从节点找到新的主库等操作,但是都是需要通过手动去执行命令。大家都知道,在线上生产环境中数据库每秒钟的不可用都会造成严重的事故&am…...

操作系统OS/存储管理/内存管理/内存管理的主要功能_基本原理_要求
基本概念 内存管理的主要功能/基本原理/要求 **内存管理的主要功能: ** 内存空间的分配与回收。由操作系统完成主存储器空间的分配和管理,使程序员摆脱存储分配的麻烦,提高编程效率。地址转换。在多道程序环境下,程序中的逻辑地…...
【手写数据库toadb】SQL解析器的实现架构,create table/insert 多values语句的解析树生成流程和输出结构分析
SQL解析器架构和实现 专栏内容: 手写数据库toadb 本专栏主要介绍如何从零开发,开发的步骤,以及开发过程中的涉及的原理,遇到的问题等,让大家能跟上并且可以一起开发,让每个需要的人成为参与者。 本专栏会定期更新,对应的代码也会定期更新,每个阶段的代码会打上tag,方…...

设计模式-备忘录模式-笔记
动机(Motivation) 在软件构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些公有接口来让其他对象得到对象的状态,便会暴露对象的细节…...

机器学习—基本术语
目录 1.样本(示例) 2.属性 3.属性值 4.属性空间 5.样本空间 6.学习(训练) 7.数据集 8.测试 9.假设 10.学习器 11.标记 12.样例 13.标记空间(样例空间) 14.分类与回归 15.有监督学习、无监督…...

pytorch单精度、半精度、混合精度、单卡、多卡(DP / DDP)、FSDP、DeepSpeed模型训练
pytorch单精度、半精度、混合精度、单卡、多卡(DP / DDP)、FSDP、DeepSpeed(环境没搞起来)模型训练代码,并对比不同方法的训练速度以及GPU内存的使用 代码:pytorch_model_train FairScale(你真…...

基于PHP的纺织用品商城系统
有需要请加文章底部Q哦 可远程调试 基于PHP的纺织用品商城系统 一 介绍 此纺织用品商城系统基于原生PHP开发,数据库mysql,前端bootstrap。用户可注册登录,购物下单,评论等。管理员登录后台可对纺织用品,用户…...
Go使用命令行输出二维码
引言 二维码(QR code)是一种矩阵条码的标准,广泛应用于商业、移动支付和数据存储等领域。在开发过程中,我们可能需要在命令行中显示二维码,这可以帮助我们快速生成和分享二维码信息。本文将介绍如何使用Go语言生成二维…...
最长连续序列[中等]
优质博文:IT-BLOG-CN 一、题目 给定一个未排序的整数数组nums,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。请你设计并实现时间复杂度为O(n)的算法解决此问题。 示例 1: 输入:nums […...

设计模式-状态模式-笔记
状态模式State 在组件构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效的管理?同时又维持高层模块的稳定?“状态变化”模式为这一问题提供了一种解决方案。 经典模式:State、Memento 动机(…...

React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...