当前位置: 首页 > article >正文

C语言超详细指针知识(一)

通过前面一段学习C语言的学习,我们了解了数组,函数,操作符等相关知识,今天我们将要进行指针学习,这是C语言中较难的一个部分,我将带你由浅入深慢慢学习。


1.内存与地址

在正式学习指针前,我们首先要了解两个概念,内存与地址。我们要如何理解它们呢?举个例子,一栋宿舍楼有不同房间,每间房门前都有属于他的门牌号,让我们可以正确的寻找到自己的宿舍。一栋宿舍楼对应一大块内存,每间宿舍对应一小块内存,地址就是门牌号,方便我们快速找到。其实我们可以把内存划分为一个个的内存单元,每个内存单元的大小是一个字节。字节是是计算机中最基本的存储单位。以下是一些常见的计算机内存单位:

bit    --比特位

Byte  --字节            1Byte = 8bit

KB                          1KB = 1024 Byte

MB                          1MB = 1024 KB

GB                           1GB = 1024 MB

TB                           1TB = 1024 GB

其中,每个内存单元,相当于一个学生宿舍,一个字节空间能放8个比特位,好比一个八人寝。每个内存单元也都有一个编号(相当于门牌号),有了这个编号(就是地址),CPU就能快速找到一个内存空间。C语言中给地址起了新的名字:指针。所以:内存单元的编号==地址==指针


2.指针变量和地址

2.1取地址操作符(&)

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

上述代码创建了整型变量a,向内存申请了四个字节的空间,用于存放整数10,每个字节都有自己的地址,整数10会优先存放在四个字节中地址较小的字节。

那么我们要如何得到a的地址呢?这就需要用到一个取地址操作符&,还是上面这个代码,我们用&a来得到了a的地址,存放到p中,打印出来:

 虽然整型变量占⽤4个字节,我们只要知道了第⼀个字节地址,顺藤摸瓜访问到4个字节的数据也是可行的。


2.2指针变量和解引用操作符

上面代码我们打印了&a的地址004FF818,这个数值是可以存储起来的,如整数存放在整型变量中,指针当然也然要存放在指针变量中,为了存放&a的地址我们用了p来充当指针变量,它的类型是int*,int*中的*实在说明p是指针变量,与整形变量做区分,而为什么是int呢?因为p地址对应的内存空间存放的是整型变量。

那如果是char类型的变量,要存放它的地址,指针变量该是什么呢?当然是char*,*做区分,char表明存放的是字符类型,如:

char ch = 'a';
char* pc = &ch;

我们用知道变量利用取地址操作符得到了地址,那么知道地址有没有对应的操作符得到变量呢?答案是肯定的,这时候就要拿出解引用操作符*了,没错,他跟上文的*一模一样,但意义截然不同,我们看代码:

 我们先创建了一个整型变量num,然后把该变量地址命名为pz,将pz解引用,赋值32,num里的值也产生了相应变化,说明我们通过*pz找到了对应23的内存空间,改变了它。

有同学肯定在想,这⾥如果⽬的就是把a改成0的话,写成 a = 0; 不就完了,为啥⾮要使⽤指针呢?其实这⾥是把a的修改交给了pa来操作,这样对a的修改,就多了⼀种的途径,写代码就会更加灵活,后期慢慢就能理解了。

2.3指针变量的大小

指针变量的大小取决于地址的大小

int main()
{//在x86环境下,指针变量的大小是四个字节printf("%zd\n", sizeof(char*));//4printf("%zd\n", sizeof(short*));//4printf("%zd\n", sizeof(int*));//4printf("%zd\n", sizeof(long*));//4//在x64环境下,指针变量的大小是四个字节printf("%zd\n", sizeof(char*));//8printf("%zd\n", sizeof(short*));//8printf("%zd\n", sizeof(int*));//8printf("%zd\n", sizeof(long*));//8return 0;
}
32位平台下地址是32个bit位,指针变量大小是4个字节
64位平台下地址是64个bit位,指针变量大小是8个字节
从代码里,我们可以明白指针变量的大小和类型是无关的,只要在相同的环境下,大小都是相同的。

指针变量的大小既然和类型无关,为什么会有各种各样的指针类型呢?我们继续接下来的学习。


我们来看一个代码:

void test1()
{int n = 0x11223344;int* pi = &n;*pi = 0;printf("%0x\n", n);//0
}void test2()
{int n = 0x11223344;char* pc = (char*)&n;*pc = 0;printf("%0x\n", n);//11223300
}int main()
{test1();test2();return 0;
}

 观察结果,我们会发现test1会将n的四个字节全部改为0,但是代码而知识将n的第一个字节改为0。

指针的类型决定了,对指针解引用的时候有多大的权限(⼀次能操作几个字节)。
比如: char* 的指针解引用就只能访问⼀个字节,而 int* 的指针的解引用就能访问四个字节。

接下来我们再来看一个代码:

我们可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。
这就是指针变量的类型差异带来的变化。
指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。

 2.4 void*指针

在指针类型中有⼀种特殊的类型是 void* 类型的,可以理解为无具体类型的指针(或者叫泛型指
针),这种类型的指针可以用来接受任意类型地址。但是也有局限性, void* 类型的指针不能直接进⾏指针的+-整数和解引用的运算。 
看如下代码:

 我们看到,void*类型的指针并不能进行想应的计算,那么void*指针到底有什么用呢?它一般使用在函数参数的部分,用来接收不同类型数据的地址,可以达到范式编程的效果。


3.const修饰

变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。
但是如果我们希望⼀个变量加上⼀些限制,不能被修改,怎么做呢?这就是const的作用。看如下代码:
上述代码中n是不能被修改的,其实n本质是变量,只不过被const修饰后,在语法上加了限制,只要我们在代码中对n进行修改,就不符合语法规则,就报错。
但是不要忘记我们是学习过指针知识,可以通过地址解引用来改变数值,这种方法可不可以绕过const修饰呢?看下面结果:

可以看到,错误变成了警告,代码得以正常运行 ,打破了const的限制。有没有办法让n通过指针解引用也无法被改变吗,当然有,我们让const修饰指针就行了,看下面代码:

代码马上又报错了,指针指向的内存空间将无法被修改,但是我们马上又有了另一个发现 :

 将const放置在指针变量类型之后,代码又可以正常运行了,这是为什么呢?

int main()
{int m = 10;m = 12;const n = 12;const int* p1 = &n;*p1 = 10;//errorp1 = &m;//rightreturn 0;
}int main()
{int m = 10;m = 12;const n = 12;int* const p1 = &n;p1 = &m;//error*p1 = 13;//rightreturn 0;
}

 在上述代码中,我们发现:

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

 4.指针运算

指针的基本运算有三种:

指针+-整数
指针-指针
指针的关系运算

4.1指针+-整数 

因为数组在内存中是连续存放的,只要知道第⼀个元素的地址,顺藤摸瓜就能找到后⾯的所有元素。

                                                             数组元素和下标 

int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p1 = arr;int sz = sizeof(arr) / sizeof(arr[0]);for (int i = 0; i < sz; i++){printf("%d ", *(p1 + i));}return 0;
}

 4.2指针减指针

int my_strlen(char* ch)
{char* p = ch;while (*p != '\0'){p++;}return p - ch;
}int main()
{printf("%d\n", my_strlen("abcdef"));return 0;
}

4.3指针的运算关系

int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);int* p = arr;while (p < arr + sz){printf("%d ", *p);p++;}return 0;
}

5.野指针

概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

5.1野指针成因 

5.1.1指针未初始化

5.1.2指针越界访问 

 5.1.3指针指向的空间释放

int* test()
{int num = 100;return &num;
}int main()
{int* p = test();printf("%d\n", *p);return 0;
}

5.2如何规避野指针

5.2.1指针初始化

如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL。NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址会报错。
int main()
{int num1 = 12;int* p1 = &num1;int* p2 = NULL;return 0;
}

5.2.2小心指针越界访问

⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是越界访问。

5.2.3指针变量不再使用时,及时置NULL,指针使用之前检查有效性 

当指针变量指向⼀块区域的时候,我们可以通过指针访问该区域,后期不再使⽤这个指针访问空间的时候,我们可以把该指针置为NULL。因为约定俗成的⼀个规则就是:只要是NULL指针就不去访问,同时使⽤指针之前可以判断指针是否为NULL。

5.2.4避免返回局部变量的地址

如造成野指针的第3个例⼦,不要返回局部变量的地址。

6.assert断言

assert.h 头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就
报错终⽌运⾏。这个宏常常被称为“断⾔”。
基本语法:
#include <assert.h>
assert(expression);

这里的 expression 是一个布尔表达式,assert 会对其进行求值。如果 expression 的值为真(非零),程序会继续正常执行;如果 expression 的值为假(零),assert 会触发一个断言失败,程序会调用 abort() 函数终止执行,并输出错误信息。 

 assert() 的使⽤对程序员是⾮常友好的,使⽤ assert() 有⼏个好处:

#define NDEBUG
#include<assert.h>

它不仅能⾃动标识⽂件和出问题的⾏号,还有⼀种⽆需更改代码就能开启或关闭 assert() 的机制。如果已经确认程序没有问题,不需要再做断⾔,就在 #include <assert.h> 语句的前⾯,定义⼀个宏 NDEBUG

然后,重新编译程序,编译器就会禁用文件中所有的 assert() 语句。如果程序又出现问题,可以移 除这条 #define NDBUG 指令(或者把它注释掉),再次编译,这样就重新启用了 assert() 语句。

assert() 的缺点是,因为引入了额外的检查,增加了程序的运行时间。

相关文章:

C语言超详细指针知识(一)

通过前面一段学习C语言的学习&#xff0c;我们了解了数组&#xff0c;函数&#xff0c;操作符等相关知识&#xff0c;今天我们将要进行指针学习&#xff0c;这是C语言中较难的一个部分&#xff0c;我将带你由浅入深慢慢学习。 1.内存与地址 在正式学习指针前&#xff0c;我们首…...

《算法笔记》3.3小节——入门模拟->图形输出

1036 跟奥巴马一起编程 #include <iostream> #include <cmath> using namespace std;int main() {int n,m;char c;cin>>n>>c;for (int i 0; i < n; i) {cout<<c;}cout<<endl;m round(1.0*n/2)-2;//round里面不能直接写n/2&#xff0c;…...

【深入浅出 Git】:从入门到精通

这篇文章介绍下版本控制器。 【深入浅出 Git】&#xff1a;从入门到精通 Git是什么Git的安装Git的基本操作建立本地仓库配置本地仓库认识工作区、暂存区、版本库的概念添加文件添加文件到暂存区提交文件到版本库提交文件演示 理解.git目录中的文件HEAD指针与暂存区objects对象 …...

在gitee上创建仓库——拉取到本地---添加文件---提交

2025/04/11/yrx0203 1-创建仓库 2-填写信息 3-创建完成后把仓库地址复制下来 4-在电脑上创建1个空的文件夹&#xff0c;进入这个文件夹&#xff0c;鼠标右击打开git bash 5-粘贴刚才复制的仓库的地址&#xff0c;回车 这样仓库就被拉取完成了 6-把本地的这个文件夹初始化…...

小刚说C语言刷题——第21讲 一维数组

在日常生活中&#xff0c;我们经常输入一组数据。例如输入一个班30名学生的语文成绩&#xff0c;或者输入一组商品的价格。这个时候&#xff0c;我们如何输入一组类型相同的数据呢&#xff1f;这里我们就要用到数组。 1.数组的概念 所谓数组就是一组相同类型数据的集合。数组中…...

芯片同时具备Wi-Fi、蓝牙、Zigbee,MAC地址会打架吗?

目录 【MAC 地址简介】 【MAC、Wi-Fi MAC、Bluetooth MAC的关系】 【以乐鑫ESP32-C6为例分析MAC】 【MAC 地址简介】 MAC&#xff08;Media Access Control&#xff09;地址是设备的物理地址&#xff0c;在全球范围内唯一标识每个网络接口。它是一个 48 比特&#xff08;6 字…...

Kotlin 学习-方法和参数类型

/*** kotlin 的方法有三种* */fun main() {/*** 方法一* 1.普通类的成员方法申明与调用* &#xff08;1&#xff09;需要先构建出实例对象&#xff0c;才能访问成员方法* &#xff08;2&#xff09;实例对象的构建只需要在类名后面加上()* */Person().test()/*** 方法二&#x…...

基于风力水力和蓄电池的低频率差联合发电系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 风力发电 4.2 风力发电 4.3 蓄电池原理 4.4 蓄电池对系统稳定性分析 5.完整工程文件 1.课题概述 基于风力水力和蓄电池的低频率差联合发电系统simulink建模与仿真。模型包括风力发电模块&#xf…...

Harmony实战之简易计算器

前言 臭宝们&#xff0c;在学会上一节的基础知识之后&#xff0c;我们来实战一下。 预备知识 我们需要用到的知识点有&#xff1a; Column组件Row组件Link装饰器button组件TextInput组件State装饰器 最终效果图 代码实现 index页面(首页) /** * program: * * descriptio…...

【Ansible自动化运维】四、ansible应用部署:加速开发到生产的流程

在软件开发的生命周期中&#xff0c;从开发到生产的应用部署过程往往是复杂且容易出错的。手动部署不仅效率低下&#xff0c;还可能引入人为错误&#xff0c;导致系统故障。Ansible 作为一款强大的自动化工具&#xff0c;能够显著简化应用部署流程&#xff0c;提高部署的准确性…...

Spring MVC 国际化机制详解(MessageSource 接口体系)

Spring MVC 国际化机制详解&#xff08;MessageSource 接口体系&#xff09; 1. 核心接口与实现类详解 接口/类名描述功能特性适用场景MessageSource核心接口&#xff0c;定义消息解析能力支持参数化消息&#xff08;如{0}占位符&#xff09;所有国际化场景的基础接口Resource…...

(十五)安卓开发中不同类型的view之间继承关系详解

在安卓开发中&#xff0c;View 是所有 UI 组件的基类&#xff0c;不同类别的 View 通过继承关系扩展和特化功能&#xff0c;以满足多样化的界面需求。以下将详细讲解常见 View 类别的继承关系&#xff0c;并结合代码示例和使用场景进行说明。 1. View 继承关系: java.lang.Obj…...

美团Leaf分布式ID生成器:雪花算法原理与应用

&#x1f4d6; 前言 在分布式系统中&#xff0c;全局唯一ID生成是保证数据一致性的核心技术之一。传统方案&#xff08;如数据库自增ID、UUID&#xff09;存在性能瓶颈或无序性问题&#xff0c;而美团开源的Leaf框架提供了高可用、高性能的分布式ID解决方案。本文重点解析Leaf…...

文件IO5(JPEG图像原理与应用)

JPEG图像原理与应用 ⦁ 基本概念 JPEG&#xff08;Joint Photographic Experts Group&#xff09;指的是联合图像专家组&#xff0c;是国际标准化组织ISO制订并于1992年发布的一种面向连续色调静止图像的压缩编码标准&#xff0c;所以也被称为JPEG标准。 同样&#xff0c;JP…...

P8682 [蓝桥杯 2019 省 B] 等差数列

题目描述 思路 让求包含这n个整数的最短等差数列&#xff0c;既让包含这几个数&#xff0c;项数最少&#xff0c;若项数最少&#xff0c;肯定不能添加小于最小的和大于最大的&#xff0c;而且让项数最小&#xff0c;公差得大 等差数列的公差aj - ai / j - i; 这又是一个等差数…...

DFS中return的作用

DFS中return的作用 在深度优先搜索(DFS)算法中&#xff0c;return语句有几个重要作用&#xff1a; 主要作用 终止当前递归分支&#xff1a;当找到解决方案或确定当前路径无效时&#xff0c;return会结束当前递归调用&#xff0c;返回到上一层。 传递结果&#xff1a;在有返回…...

Java 8 响应式编程:用函数式风格优雅地处理异步流

引言&#xff1a;响应式编程是什么&#xff1f; 响应式编程&#xff08;Reactive Programming&#xff09;是一种异步编程范式&#xff0c;它允许你以流的方式处理数据流和事件流&#xff0c;强调数据的流动与变化。通过响应式编程&#xff0c;你可以以声明式的方式构建应用&a…...

《Vue3学习手记》

下面进入Vue3的学习&#xff0c;以下代码中都有很详细的注释&#xff0c;代码也比较清晰易懂&#xff1a; Vue3 index.html是入口文件 Vue3通过createApp函数创建一个应用实例 main.ts: // Vue3中通过createApp函数创建应用实例 // 引入createApp用于创建应用 import { crea…...

批量给文件编排序号,支持数字序号及时间日期序号编排文件

当我们需要对文件进行编号的时候&#xff0c;我们可以通过这个工具来帮我们完成&#xff0c;它可以支持从 001 到 100 甚至更多的数字序号编号。也可以支持按照日期、时间等方式对文件进行编号操作。这是一种操作简单&#xff0c;处理起来也非常的高效文件编排序号的方法。 工作…...

LLM_基于OpenAI的极简RAG

一、RAG主要流程 #mermaid-svg-gXjcqQe5kyb41Yz2 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-gXjcqQe5kyb41Yz2 .error-icon{fill:#552222;}#mermaid-svg-gXjcqQe5kyb41Yz2 .error-text{fill:#552222;stroke:#55…...

Dynamics365 ExportPdfTemplateExportWordTemplate两个Action调用的body构造

这两天在用ExportPdfTemplate做pdf导出功能时&#xff0c;遇到了如下问题InnerException : Microsoft.OData.ODataException: An unexpected StartArray node was found when reading from the JSON reader. A PrimitiveValue node was expected. 我的场景是使用power automate…...

Java 程序调试与生产问题排查工具Arthas

好的&#xff0c;以下是修改后的博客内容&#xff0c;将公司信息替换为通用的占位符&#xff1a; 深入探索 Arthas&#xff1a;Java 程序调试与生产问题排查的利器 在 Java 开发中&#xff0c;调试和诊断问题往往是一个复杂且耗时的过程。Arthas&#xff08;Alibaba Java Dia…...

day26图像处理OpenCV

文章目录 一、OpenCV1.介绍2.下载3.图像的表示4.图像的基本操作4.1图片读取或创建4.1.1读取4.1.2创建 4.2创建窗口4.3显示图片4.3.1设置读取的图片4.3.2设置显示多久4.3.3释放 4.4.保存图片4.5图片切片&#xff08;剪裁&#xff09;4.6图片大小调节 5.在图像中绘值5.1绘制直线5…...

国际物流怎么找客户?选择适合自己的企业拓客平台

在国际物流行业&#xff0c;获客一直是企业发展的核心难题。无论是跨境电商、传统外贸&#xff0c;还是国际货代&#xff0c;找到精准的客户资源并高效转化&#xff0c;是决定企业能否抢占市场蓝海的关键。今天&#xff0c;我们就来聊聊如何选择一个真正适合的国际物流拓客平台…...

2025年Y1大型游乐设施修理证报考要求

Y1大型游乐设施修理证是从事大型游乐设施维修、保养的必备资质&#xff0c;由国家市场监督管理总局颁发。报考需满足以下条件&#xff1a; 1. 基本条件 年龄&#xff1a;18周岁以上&#xff0c;60周岁以下&#xff1b; 学历&#xff1a;初中及以上文化程度&#xff1b; 健康…...

第四十六篇 人力资源管理数据仓库架构设计与高阶实践

声明&#xff1a;文章内容仅供参考&#xff0c;需仔细甄别。文中技术名称属相关方商标&#xff0c;仅作技术描述&#xff1b;代码示例为交流学习用途&#xff1b;案例数据已脱敏&#xff0c;技术推荐保持中立&#xff1b;法规解读仅供参考&#xff0c;请以《网络安全法》《数据…...

分布式ID生成算法:雪花算法和UUID

在分布式系统中&#xff0c;生成全局唯一ID是核心需求之一。雪花算法和UUID是两种广泛使用的解决方案。 1. 雪花算法 工作原理 分布式ID生成器&#xff1a;由Twitter开源&#xff0c;专为分布式系统设计。组成结构&#xff08;64位二进制&#xff09;&#xff1a; 符号位&…...

高效查询Redis中大数据的实践与优化指南

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务) &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1;个人微信&a…...

操作系统 4.2-键盘

键盘中断初始化和处理 提取的代码如下&#xff1a; // con_init 函数&#xff0c;初始化控制台&#xff08;包括键盘&#xff09;的中断 void con_init(void) {set_trap_gate(0x21, &keyboard_interrupt); } ​ // 键盘中断处理函数 .globl _keyboard_interrupt _keyboard…...

STM32+EC600E 4G模块 与华为云平台通信

前言 由于在STM32巡回研讨会上淘了一块EC600E4G模块以及刚办完电信卡多了两张副卡&#xff0c;副卡有流量刚好可以用一下&#xff0c;试想着以后画一块ESP32板子搭配这个4G模块做个随身WIFI&#xff0c;目前先用这个模块搭配STM32玩一下云平顺便记录一下。 实验目的 实现STM…...