初阶C语言——指针【详解】
文章目录
- 1.指针是什么
 - 2.指针和指针类型
 - 2.1 指针的解引用
 - 2.2 指针+ -整数
 
- 3.野指针
 - 3.1 野指针成因
 - 3.2 如何规避野指针
 
- 4. 指针运算
 - 4.1 指针+-整数
 - 4.2 指针-指针
 - 4.3 指针的关系运算
 
- 5. 指针和数组
 - 6. 二级指针
 - 7. 指针数组
 
1.指针是什么
指针理解的2个要点:
- 指针是内存中一个最小单元的编号,也就是地址
 - 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
 
总结: 指针就是地址,口语中说的指针通常指的是指针变量。
我们可以这样理解:
 内存:
 
指针变量
我们可以通过&(取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个变量就是指针变量
看代码:
#include <stdio.h>
int main()
{int a = 10;//在内存中开辟一块空间int* p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。//a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量中,p就是一个之指针变量。return 0;
}
 
总结:
指针变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)。
经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。
 对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电平(低电压)就是(1或者0);
 那么32根地址线产生的地址就会是:
 
 这里我们就明白:
- 在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
 - 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
 
总结:
- 指针变量是用来存放地址的,地址是唯一标示一个内存单元的
 - 指针的大小在32位平台是4个字节,在64位平台是8个字节
 
2.指针和指针类型
char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;
 
指针的定义方式是: type + * 。
 char* 类型的指针是为了存放 char 类型变量的地址。
 short* 类型的指针是为了存放 short 类型变量的地址。
 int* 类型的指针是为了存放 int 类型变量的地址。
2.1 指针的解引用
代码演示
#include <stdio.h>
int main()
{int n = 0x11223344;char* pc = (char*)&n;int* pi = &n;*pc = 0; //重点在调试的过程中观察内存的变化。*pi = 0; //重点在调试的过程中观察内存的变化。return 0;
}
 
总结:
 指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。
 比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。
2.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;
}
 
运行结果:
 
 总结: 指针的类型决定了指针向前或者向后走一步有多大(距离)。
3.野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
3.1 野指针成因
- 指针未初始化
 
#include <stdio.h>
int main()
{int* p;//局部变量指针未初始化,默认为随机值*p = 20;return 0;
}
 
代码运行之后会报错
 
- 指针越界访问
 
#include <stdio.h>
int main()
{int arr[10] = { 0 };int* p = arr;int i = 0;for (i = 0; i <= 11; i++){//当指针指向的范围超出数组arr的范围时,p就是野指针*(p++) = i;}return 0;
}
 
- 指针指向的空间释放
 
#include <stdio.h>
int* test()
{int a = 10;return &a;
}
int main()
{int*p = test();*p = 100;return 0;
}
 
 a出test函数就已经销毁,但是return把a的地址传给了p,p里边存放了a的地址,但是p不能在对其进行修改
 
3.2 如何规避野指针
- 指针初始化
 - 小心指针越界
 - 指针指向空间释放,及时置NULL
 - 避免返回局部变量的地址
 - 指针使用之前检查有效性
 
#include <stdio.h>
int main()
{//一个指针不知道应该指向哪里的时候,暂时可以初始化为NULL;int* p = NULL;if (p != NULL)//判断指针是否为空,不为空再进行访问{*p = 100;}return 0;
}
 
4. 指针运算
4.1 指针±整数
代码演示:
#include <stdio.h>int my_strlen(char * str)
{int count = 0;while (*str != '\0'){count++;//指针+整数str = str + 1;}return count;
}int main()
{int len = my_strlen("abcdef");printf("%d\n", len);return 0;
} 
运行结果
 
4.2 指针-指针
指针-指针=地址-地址
代码演示:
#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int n = &arr[9] - &arr[0];printf("%d\n", n);return 0;
}
 
运行结果:
 

 总结: 指针加整数等于指针,指针减指针等于整数
4.3 指针的关系运算
//代码一
#define N_VALUES 5
float values[N_VALUES];
float* vp;
//指针关系的运算
for (vp = &values[N_VALUES]; vp > &values[0];)
{*--vp = 0;
}
 

 
通过- -vp把values数组改成0
 代码简化
//代码二:
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{*vp = 0;
}
 
让vp指向下标为4的元素,通过- -vp把values数组改成0
代码二实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证
 它可行。
 标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与
指向第一个元素之前的那个内存位置的指针进行比较。

 允许p1与p3进行比较,不允许p1与p2进行比较
5. 指针和数组
指针就是指针,不是数组
数组就是数组,也不是指针

 指针和数组的关系:
指针是可以指向数组元素的
因为指针可以运算,所以借助指针可以访问数组
代码演示:
#include <stdio.h>
int main()
{int arr[10] = { 0 };int* p = arr;//指针存放数组首元素的地址int i = 0;//存放for (i = 0; i < 10; i++){*p = i + 1;p++;}//打印p = arr;for (i = 0; i < 10; i++){printf("%d ", *(p + i));}return 0;
}
 
运行结果:
 
6. 二级指针
指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?
 这就是 二级指针 。
 代码演示:
#include <stdio.h>
int main()
{int a = 10;//a是要在内存中申请4个字节的空间的//一级指针int* pa = &a;//0x0012ff40, pa是指针变量,用来存放地址,也得向内存申请,申请4/8//二级指针int** ppa = &pa;//0x0012ff48printf("%d\n", **ppa);return 0;
}
 
运行结果:
 
 
二级指针的运算
- *ppa 通过对ppa中的地址进行解引用,这样找到的是 pa , *ppa 其实访问的就是 pa。
 
int b = 20;
*ppa = &b;//等价于 pa = &b;
 
- **ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a。
 
**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;
 
7. 指针数组
指针数组是指针还是数组?
 答案:是数组。是存放指针的数组。
 代码演示:
#include <stdio.h>
int main()
{int a = 10;int b = 20;int c = 30;int* arr[] = { &a,&b,&c };int i = 0;for (i = 0; i < 3; i++){printf("%d ", *(arr[i]));}return 0;
}
 
运行结果:
 
 指针数组与二级指针的结合
#include <stdio.h>
int main()
{char* arr[5];//[char* char* char* char* char*]char** p = arr;//&arr[0] - char**return 0;
}
 
用一维数组模拟出一个二维数组
 看代码:
#include <stdio.h>
int main()
{int arr1[] = { 1,2,3,4,5 };int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };int* ptr[] = { arr1,arr2,arr3 };int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j< 5; j++){printf("%d ",ptr[i][j]);}printf("\n");}return 0;
}
 
运行结果:

相关文章:
初阶C语言——指针【详解】
文章目录1.指针是什么2.指针和指针类型2.1 指针的解引用2.2 指针 -整数3.野指针3.1 野指针成因3.2 如何规避野指针4. 指针运算4.1 指针-整数4.2 指针-指针4.3 指针的关系运算5. 指针和数组6. 二级指针7. 指针数组1.指针是什么 指针理解的2个要点: 指针是内存中一个最…...
MySQL tinyint(1) 、int(32) 与 varchar(255) 长度含义不同
MySQL tinyint(1) 、int(32) 与 varchar(255) 长度含义不同 发现 tinyint(1),int(32) 和 varchar(255) 这里面的数字的含义是不同的。 先说数字类型 tinyint 和 int 等 他们能存储的字节大小是与类型绑定的,即定义了 tinyint 或者 int 就确定了能存储…...
搜索旋转排序数组、路径总和 II、拆分数字
文章目录搜索旋转排序数组(数组、二分查找)路径总和 II(树、深度优先搜索)拆分数字(算法)搜索旋转排序数组(数组、二分查找) 整数数组 nums 按升序排列,数组中的值 互不…...
QT自绘标题和边框
在QT中如果想要自绘标题和边框,一般步骤是: 1) 在创建窗口前设置Qt::FramelessWindowHint标志,设置该标志后会创建一个无标题、无边框的窗口。 2)在客户区域的顶部创建一个自绘标题栏。 3)给窗口绘制一个背…...
数据库浅谈之 LLVM
数据库浅谈之 LLVM HELLO,各位博友好,我是阿呆 🙈🙈🙈 这里是数据库浅谈系列,收录在专栏 DATABASE 中 😜😜😜 本系列阿呆将记录一些数据库领域相关的知识 Ἴ…...
Unable to connect to Redis无法连接到Redis
文章目录项目场景:问题描述原因分析:解决方案:项目场景: 提示:这里简述项目相关背景: 在某个项目中的提交按钮不好用 org.springframework.data.redis.RedisConnectionFailureException: Unable to con…...
Feign、Ribbon、Hystrix
🏆今日学习目标: 🍀Feign、Ribbon、Hystrix ✅创作者:林在闪闪发光 ⏰预计时间:30分钟 🎉个人主页:林在闪闪发光的个人主页 🍁林在闪闪发光的个人社区,欢迎你的加入: 林在…...
SpringCloud - Nacos注册发现
目录 服务注册到Nacos 服务分级存储模型 NacosRule负载均衡 服务实例的权重设置 环境隔离 Nacos与Eureka的对比 添加Nacos配置 微服务配置拉取 配置热更新 多环境配置共享 服务注册到Nacos 1.在父工程引入SpringCloudAlibaba的依赖 2.注释掉order-service和user-ser…...
Socket编程、协议理解
Socket编程、协议理解简单说明Socket编程Socket 常用接口Socket服务端业务编码代码说明文件服务(fileServe)消息服务(msgServe)消息写会(writeResponse)注意事项Socket客户端业务编码代码说明总结RYP协议:基于TCP/IP协议之上的自定义协议Rocky Protocol。 协议用途&…...
Idea集成码云
1:Idea集成码云1.1:IDEA安装码云插件【第一步】Idea 默认不带码云插件, 我们第一步要安装 Gitee 插件。如图所示, 在 Idea 插件商店搜索 Gitee,然后点击右侧的 Install 按钮。安装成功后,重启 Idea。Idea 重…...
并发编程学习篇ReentrantLock设计思想剖析
一、AQS原理剖析 什么是AQS java.util.concurrent包中的大多数同步器实现都是围绕着共同的基础行为,比如 等待队列、条件队列、独占获取、共享获取等而这些行为的抽象就是基于AbstractQueuedSynchronizer(简称AQS)实现的,AQS是一…...
区分效度全流程分析
基本说明 区分效度(又称判别效度、区别效度),其实质也是一种结构效度。区分效度强调本不应该在同一因子的测量项,确实不在同一因子下面。比如说,测量项A和 B分别测量两个属性,应该分属于因子A和因子B中&…...
【华为OD机试模拟题】用 C++ 实现 - 找数字(2023.Q1)
最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…...
从0开始写Vue项目-Vue实现用户数据批量上传和数据导出
从0开始写Vue项目-环境和项目搭建_慕言要努力的博客-CSDN博客从0开始写Vue项目-Vue2集成Element-ui和后台主体框架搭建_慕言要努力的博客-CSDN博客从0开始写Vue项目-Vue页面主体布局和登录、注册页面_慕言要努力的博客-CSDN博客从0开始写Vue项目-SpringBoot整合Mybatis-plus实现…...
企业容器云管理平台选型指南
作者简介 涂家英,SUSE 资深架构师,专注 Cloud-Native 相关产品和解决方案设计,在企业级云原生平台建设领域拥有丰富的经验。 数字时代下的容器云管理平台 数字时代,市场竞争加剧,业务需求日新月异,敏态 IT…...
OpenGL超级宝典学习笔记:着色器存储区块、原子内存操作、内存屏障
前言 本篇在讲什么 本篇为蓝宝书学习笔记 着色器存储区块 原子内存操作 内存屏障 本篇适合什么 适合初学Open的小白 本篇需要什么 对C语法有简单认知 对OpenGL有简单认知 最好是有OpenGL超级宝典蓝宝书 依赖Visual Studio编辑器 本篇的特色 具有全流程的图文教学 重…...
SpringMVC框架知识详解(入门版)
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
25-动画和过渡
动画和过渡 一、动画 使用css动画样式,配合vue实现动画效果。 编写模板 <template><div><button click"isShow !isShow">显示/隐藏</button><h1 v-show"isShow">你好啊</h1></div> </templa…...
Linux 操作系统原理 — 虚拟内存管理
目录 文章目录 目录虚拟内存技术页式内存管理技术x86_32 CPU 虚拟内存虚拟地址格式与内核页表虚拟内存空间Kernel SpaceUser Spacex86_64 CPU 虚拟内存虚拟地址格式与内核页表(四级页表)虚拟内存空间TLB 缓冲(快表)进程页表虚拟内存技术 虚拟内存技术是操作系统实现的一种…...
保持超低温环境新方法:功耗降至十分之一!
(图片来源:网络)量子比特是量子计算机的主要构建部分,然而热量会导致量子比特容易出错,因此量子系统通常保存在超低温稀释制冷机内,可以将温度保持在绝对零度(−273.15℃)以上。但是…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...
【C++】纯虚函数类外可以写实现吗?
1. 答案 先说答案,可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...
uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)
UniApp 集成腾讯云 IM 富媒体消息全攻略(地理位置/文件) 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型,核心实现方式: 标准消息类型:直接使用 SDK 内置类型(文件、图片等)自…...
上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式
简介 在我的 QT/C 开发工作中,合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式:工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...
算法—栈系列
一:删除字符串中的所有相邻重复项 class Solution { public:string removeDuplicates(string s) {stack<char> st;for(int i 0; i < s.size(); i){char target s[i];if(!st.empty() && target st.top())st.pop();elsest.push(s[i]);}string ret…...
