指针详解之 多层嵌套的关系
1 例子之指向3个字符串的指针数组,易混淆!
1.1过程详解:
char *str[3]={ "Hello,thisisasample!", "Hi,goodmorning.", "Helloworld"
};
char s[80];
strcpy(s,str[0]); //也可写成strcpy(s,*str);
strcpy(s,str[1]); //也可写成strcpy(s,*(str+1));
strcpy(s,str[2]); //也可写成strcpy(s,*(str+2));
上例中,str 是一个三单元的数组,该数组的每个单元都是一个指针,这些指针各指向一个字符串。把指针数组名str 当作一个指针的话,它指向数组的第0 号单元,它的类型是char **,它指向的类型是char *。
*str 也是一个指针,它的类型是char *,它所指向的类型是char,它指向的地址是字符串"Hello,thisisasample!"的第一个字符的地址,即'H'的地址。注意:字符串相当于是一个数组,在内存中以数组的形式储存,只不过字符串是一个数组常量,内容不可改变,且只能是右值.如果看成指针的话,他即是常量指针,也是指针常量.
str+1 也是一个指针,它指向数组的第1 号单元,它的类型是char**,它指向的类型是char*。
*(str+1)也是一个指针,它的类型是char*,它所指向的类型是char,它指向"Hi,goodmorning."的第一个字符'H'
1.2 分析:哪里易错
在你提供的代码片段中,char *str[3] 定义了一个包含三个元素的字符指针数组,每个元素都是一个指向字符(char)的指针。这些指针分别初始化为指向三个字符串字面量。
现在我们具体来看 str 和 *str 在这种上下文中的含义:
1 str/*str 的指针类型、指向的类型、内部的值
- 类型:
str是一个指针数组,其类型是char *[3],也就是说它是一个含有三个char *类型元素的数组。 - 值:
str本身代表这个数组的起始地址,即第一个指针str[0]的地址。因此,str可以被视为一个指向char *类型的指针,即char **。 - 使用场景:当你使用
str时,你可以通过索引来访问数组中的各个指针,例如str[0]、str[1]和str[2],它们分别指向不同的字符串。
2 *str 或 str[0]
- 类型:
*str是对str进行一次解引用的结果。由于str是一个char *[3]类型的数组,*str的类型就是char *,也就是指向字符的指针。 - 值:
*str等同于str[0],它指向了第一个字符串"Hello,thisisasample!"的第一个字符'H'。 - 使用场景:当你使用
*str时,实际上是在访问str数组的第一个元素,也就是指向第一个字符串的指针。如果你想访问该字符串中的字符,你可以进一步解引用,例如**str或(*str)[0]将会得到字符'H'。
3 具体例子
假设你有以下代码:
c 深色版本
#include <stdio.h>int main() {char *str[3] = { "Hello,thisisasample!", "Hi,goodmorning.", "Helloworld" };// 打印 str 的值,即指针数组的地址printf("Address of str: %p\n", (void *)str);// 打印 *str 的值,即第一个指针所指向的地址printf("Address pointed by *str: %p\n", (void *)*str);// 打印 *str 解引用后的值,即第一个字符串的第一个字符printf("First character of the first string: %c\n", **str);// 打印第二个指针所指向的字符串printf("Second string: %s\n", str[1]);return 0;
}
这段代码将会输出类似如下的结果(具体的内存地址会有所不同):
深色版本
Address of str: 0x7ffee4b8a960
Address pointed by *str: 0x105f41010
First character of the first string: H
Second string: Hi,goodmorning.
在这个例子中,str 表示整个指针数组的地址,而 *str 表示数组中第一个元素(即第一个字符串的指针)的值。**str 则是进一步解引用,得到了第一个字符串的第一个字符。
3.数组和结构体
3.1结构体用法
struct book{/* data */char name[MaxL];char press[MaxL];float price;};char *str[3] = {"123","456","789"};// printf("%s \n",typeof(*str));// printf("%s \n",typeof(str));struct book zgs = {"哈佛史","asml出版社",100};printf(" 名字:%s \n 出版社:%s \n 价格:%f 'n",zgs.name,zgs.press,zgs.price);
3.1 结构体易错点
不能直接结构体的指针赋值:必须要用strcpy(s1.name,"张三");
3.2 代码:
#include<stdio.h>
#include<string.h>int main()
{struct student{char name[20];int age;char sex;};struct student s1 ;strcpy(s1.name,"zhangsan");s1.age = 19;s1.sex = 'm';printf("%s %d %c",s1.name,s1.age,s1.sex);// // {// "zhangsan",// 18,// 'm'// }return 0 ;
}
4 指针和函数
4.1求一个字符串的ascii码之和
#include <stdio.h>int fun(char *s)
{int num = 0;for (int i = 0; *s!= '\0';){num += *s;s++;}return num;
}
int main(void)
{char str[] = "asdflkadlkgaslkdlkdfsjlkdsfjdlkfj87UHJNBN*&^^)(*&^) &**&^TYH";// int fun(char *str );// int num = 0;int out = fun(str);printf("%d\n", out);return 0;
}
截图:
函数申明、引用、循环条件
5.指针安全问题
5.1指针类型转换与越界写入
char s = 'a';
int *ptr;
ptr = (int *)&s;
*ptr = 1298;
1. 代码解析
- 变量声明:
char s = 'a';声明了一个字符变量s,并将其初始化为字符'a'。在内存中,s占用一个字节。 - 指针声明:
int *ptr;声明了一个指向int类型的指针ptr。 - 类型转换:
ptr = (int *)&s;将s的地址强制转换为int *类型,并赋值给ptr。这意味着ptr现在指向s的首地址,但它的类型是int *,而不是char *。 - 写入操作:
*ptr = 1298;试图通过ptr写入一个整数值1298到s所在的内存位置。
2. 问题分析
a. 内存布局
在32位系统中,int 类型占用4个字节,而 char 类型只占用1个字节。因此,*ptr = 1298; 这条语句不仅仅是改变了 s 所占的一个字节,还会同时改变 s 后面相邻的三个字节。具体来说:
s占用的内存位置假设为0x1000。*ptr = 1298;实际上会将1298(即0x00000512)写入从0x1000开始的四个字节中:0x1000:0x12(最低有效字节)0x1001:0x050x1002:0x000x1003:0x00(最高有效字节)
b. 越界写入的影响
由于 s 只占用一个字节,而 *ptr = 1298; 写入了四个字节,因此会覆盖 s 后面的三个字节。这些字节可能是其他变量、数据结构的一部分,甚至是程序的代码段或栈中的重要数据。具体影响取决于这些字节的内容:
- 覆盖其他变量:如果
s后面的三个字节属于其他变量,那么这些变量的值会被意外修改,导致程序行为异常。 - 破坏栈帧:如果
s是局部变量,位于栈中,那么*ptr = 1298;可能会破坏栈帧,导致函数返回地址或其他栈上的数据被篡改,进而引发程序崩溃或未定义行为。 - 覆盖代码段:在某些情况下,
*ptr = 1298;可能会覆盖程序的代码段,导致程序执行非法指令,直接崩溃或产生不可预测的行为。
c. 未定义行为
C语言标准规定,当程序访问未分配的内存或超出变量范围时,会导致未定义行为(undefined behavior)。未定义行为意味着编译器和运行时环境可以以任何方式处理这种情况,包括但不限于程序崩溃、数据损坏、甚至看似正常运行但实际上隐藏了严重的安全隐患
3. 避免此类错误的建议
为了避免这种类型的错误,开发者应该遵循以下最佳实践:
a. 避免不必要的类型转换
类型转换(尤其是强制类型转换)应该谨慎使用。在本例中,将 char * 强制转换为 int * 是不安全的,因为它忽略了类型系统的约束,导致越界写入。如果确实需要进行类型转换,应该确保目标类型和源类型的大小一致,并且不会导致内存越界。
b. 使用适当的数据类型
选择合适的数据类型来存储和操作数据。如果只需要操作单个字符,应该使用 char 类型的指针,而不是 int *。这样可以避免不必要的内存访问和越界风险。
c. 检查指针的有效性
在使用指针之前,始终检查它是否指向有效的内存区域。可以通过以下方式减少错误:
- 初始化指针:在声明指针时,确保它被正确初始化为
NULL或指向有效的内存地址。 - 释放内存后置空:在释放动态分配的内存后,立即将指针设为
NULL,防止悬垂指针18
。 - 边界检查:在对数组或缓冲区进行操作时,确保指针不会超出其合法范围。
d. 使用现代工具和技术
现代编程语言和工具提供了许多机制来帮助开发者避免指针误用:
- 静态分析工具:使用静态代码分析工具(如 Clang Static Analyzer、Cppcheck 等)可以在编译时检测潜在的指针错误。
- 编译器警告:启用编译器的所有警告选项,并确保修复所有警告。编译器通常会提示可能的指针误用或类型不匹配问题。
e. 理解内存模型
深入了解系统的内存模型和字节序(endianness)对于编写正确的指针操作代码至关重要。不同的系统可能有不同的字节序(大端或小端),这会影响多字节数据的存储顺序。例如,在小端系统中,int 类型的值 1298 会被存储为 0x12 0x05 0x00 0x00,而在大端系统中则是 0x00 0x00 0x05 0x12。如果不了解这一点,可能会导致跨平台移植时出现错误
相关文章:
指针详解之 多层嵌套的关系
1 例子之指向3个字符串的指针数组,易混淆! 1.1过程详解: char *str[3]{ "Hello,thisisasample!", "Hi,goodmorning.", "Helloworld" }; char s[80]; strcpy(s,str[0]); //也可写成strcpy(s,*st…...
Animated Drawings:让纸上的角色动起来
前言 今天介绍的这个工具非常的有意思:它可以让我们在纸上绘画的角色动起来。先一起来看看效果: 准备 首先,我们先准备一张绘画。可以在纸上进行绘制,也可以在电子设备上进行绘制。绘制内容不限,在这里为了方便演示&am…...
技术与教育的结合:高校听课评价系统的设计与实施
3.1系统可行性分析 需要使用大部分精力开发的高校听课评价系统为了充分降低开发风险,特意在开发之前进行可行性分析这个验证系统开发是否可行的步骤。本文就会从技术角度,经济角度,还有用户使用的程序的运行角度进行综合阐述。 3.1.1 技术可行…...
web移动端项目常用解决方案
移动端总会遇到一系列特定于移动设备的问题,分享下常见的移动端常见问题的处理方案。 1px边框问题 在高清屏幕下,1px的边框显示得比较粗。 .border-1px {position: relative; } .border-1px::after {position: absolute;content: ;width: 200%;height:…...
LabVIEW软件项目设计方案如何制定
制定LabVIEW软件项目设计方案需要综合考虑需求分析、架构设计、功能模块划分和时间预算等多个方面,确保项目开发过程高效、可控且最终满足目标要求。以下是一个详细的制定流程: 1. 需求分析 目标定义:明确项目的目标,例如数据采…...
数据结构(Java)——链表
1.概念及结构 链表是一种 物理存储结构上非连续 存储结构,数据元素的 逻辑顺序 是通过链表中的 引用链接 次序实现的 。 2.分类 链表的结构非常多样,以下情况组合起来就有 8 种链表结构: (1)单向或者双向 (…...
变量与数据类型 - 整型、浮点型、字符型等
引言 在编程中,变量和数据类型是基础中的基础。理解它们如何工作以及如何正确使用它们对于编写高效且无误的代码至关重要。本文将详细介绍 C 中的几种基本数据类型:整型、浮点型、字符型等,并通过实例帮助读者更好地理解和掌握这些概念。 一…...
MacOS安装Xcode(非App Store)
文章目录 访问官网资源页面 访问官网资源页面 直接访问官网的历史版本下载资源页面地址:https://developer.apple.com/download/more/完成APP ID的登陆,直接找到需要的软件下载即可 解压后,安装将xcode.app移动到应用程序文件夹。...
运行Zr.Admin项目(后端)
1.下载Zr.Admin代码压缩包 https://codeload.github.com/izhaorui/Zr.Admin.NET/zip/refs/heads/main 2.打开项目 我这里装的是VS2022社区版 进入根目录,双击ZRAdmin.sln打开项目 3.安装.net7运行时 我当时下载的代码版本是.net7的 点击安装 点击安装࿰…...
Ubuntu24.04最新版本安装详细教程
Ubuntu 24.04 LTS发布说明 推荐的系统配置要求: 双核2 GHz处理器或更高 4 GB系统内存 25 GB磁盘存储空间 可访问的互联网 光驱或USB安装介质 Ubuntu 24.04官方下载网址:https://cn.ubuntu.com/download/desktop 04. Ubuntu 22.04(创建虚拟机方式一) 4…...
js版本之ES6特性简述【Proxy、Reflect、Iterator、Generator】(五)
目录 Proxy Reflect 静态方法 部分实例 Iterator 实际开发迭代器的使用实例 迭代器(Iterator)应用 Generator Proxy Proxy 是 ES6 中新增的对象 Proxy 是JavaScript中的内置对象,它提供了一种机制,可以拦截并自定义各种…...
CSS实现一个自定义的滚动条
要使用CSS创建一个自定义的滚动条,你可以使用伪元素和CSS的伪类来控制滚动条的外观和行为。以下是一个简单的例子,展示如何为任何HTML元素添加一个自定义的滚动条样式: <!DOCTYPE html> <html lang"en"> <head> …...
CKA认证 | Day8 K8s安全
第八章 Kubernetes安全 1、Kubernetes RBAC授权 Kubernetes 基于角色的访问控制(Role-Based Access Control, RBAC) 是一种强大的权限管理机制,用于控制用户、用户组、服务账户对 Kubernetes 集群资源的访问。通过 RBAC,可以细…...
深度分析java 使用 proguard 如何解析混淆后的堆栈
经过proguard混淆过后,发生异常时堆栈也进行了混淆,那么如果获取的原始的堆栈呢?我们下面来看下 使用proguard 根据mapping文件直接解析 import proguard.obfuscate.MappingReader; import proguard.retrace.FrameInfo; import proguard.re…...
bash 中 ${-#*i} 是什么意思?
-------------------------------------------------- author: hjjdebug date: 2024年 12月 25日 星期三 17:43:45 CST description: bash 中 ${-#*i} 是什么意思? -------------------------------------------------- 在centos 的 /etc/profile 中有这样的语句 for i in /…...
什么是Top-p采样与Top-k采样?大模型推理时如何同时设置?解析Transformers库源代码
什么是Top-p采样与Top-k采样?大模型推理时如何同时设置? 在自然语言生成(NLG)和大规模语言模型推理中,Top-k采样 和 Top-p采样 是两种常用的解码策略,用于控制生成文本的多样性和质量。它们在生成过程中对…...
java队列--数据结构
文章目录 前言本文源代码网址:https://gitee.com/zfranklin/java/tree/master/dataStructure/src/com/njupt/queue队列的性质数组队列成员变量方法 链表栈成员变量方法 总结 前言 顺序表和链表两种存储方式实现数据结构–队列。 本文源代码网址:https:/…...
【WebSocket】tomcat内部处理websocket的过程
websocket请求格式 浏览器请求 GET /webfin/websocket/ HTTP/1.1。 Host: localhost。 Upgrade: websocket。 Connection: Upgrade。 Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg。 Origin: http://服务器地址。 Sec-WebSocket-Version: 13。 服务器响应 HTTP/1.1 101 Swi…...
【踩坑/Linux】Vmware中的Ubuntu虚拟机无法访问互联网
Vmware中的Ubuntu虚拟机无法访问互联网 首先前提是我的系统是Ubuntu 16.04系统,vmware workstation选择的是NAT模式,虚拟机内连不上网络 ping www.baidu.com ping: unknown host www.baidu.com首先检查 DNS 解析服务:在虚拟机中打开命令提示…...
overleaf中的includegraphics设置图片缩放,居中显示
overleaf中的includegraphics设置图片缩放,居中显示 \includegraphics[width=0.5\textwidth]{example.jpg} \centering 在使用 \includegraphics 命令插入图片时,可以通过设置其参数来缩小图片的显示尺寸,以下是几种常见的方法: 设置宽度或高度 按比例缩小宽度:可以使用…...
【2026年最新600套毕设项目分享】springboot足球训练营系统(14309)
有需要的同学,源代码和配套文档领取,加文章最下方的名片哦 一、项目演示 项目演示视频 二、资料介绍 完整源代码(前后端源代码SQL脚本)配套文档(LWPPT开题报告/任务书)远程调试控屏包运行一键启动项目&…...
QuickBMS深度解析:游戏资源提取与逆向工程的终极工具箱
QuickBMS深度解析:游戏资源提取与逆向工程的终极工具箱 【免费下载链接】QuickBMS QuickBMS by aluigi - Github Mirror 项目地址: https://gitcode.com/gh_mirrors/qui/QuickBMS 在游戏开发和逆向工程领域,面对数百种不同的压缩格式、加密算法和…...
FinalBurn Neo技术指南:现代设备复刻街机厅沉浸体验全攻略
FinalBurn Neo技术指南:现代设备复刻街机厅沉浸体验全攻略 【免费下载链接】FBNeo FinalBurn Neo - We are Team FBNeo. 项目地址: https://gitcode.com/gh_mirrors/fb/FBNeo 如何在现代设备上复刻街机厅的沉浸体验?FinalBurn Neo(FBN…...
根据给定文本内容,适合的标题可以是:“‘三泵排水电气控制系统及组态设计的梯形图、接线图原理图”...
自动排水控制设计3泵排水三泵排水电气控制系统排水组态 我们主要的后发送的产品有,带解释的梯形图接线图原理图图纸,io分配,组态画面每逢暴雨天,物业师傅盯着排水泵的手机都要刷出火星子——生怕哪台泵罢工,地下室直…...
Mojo调用PyTorch模型推理却遭遇内存泄漏?——国家级实验室验证的4层内存隔离架构首次公开
第一章:Mojo调用PyTorch模型推理却遭遇内存泄漏?——国家级实验室验证的4层内存隔离架构首次公开在高性能AI边缘部署场景中,Mojo语言通过其零开销FFI机制调用PyTorch C前端(LibTorch)实现低延迟推理,但实测…...
能源在线监测管理系统平台[fu源码]
EMS能源管理系统 基于 Vue3 / Spring Boot/Spring Cloud & Alibaba 微服务架构 项目技术框架 RuoYi-Cloud 基础框架上开发而成 源智优控AI能源大脑,能源AI版,即将上线 仓库地址: https://gitee.com/guangdong122/energy-management …...
NVIDIA Profile Inspector完全掌握:从问题诊断到性能优化的效率提升指南
NVIDIA Profile Inspector完全掌握:从问题诊断到性能优化的效率提升指南 【免费下载链接】nvidiaProfileInspector 项目地址: https://gitcode.com/gh_mirrors/nv/nvidiaProfileInspector 许多玩家和创作者拥有高性能NVIDIA显卡,却因配置不当无法…...
YOLO12应用场景:零售货架识别中商品计数+品类分类一体化方案
YOLO12应用场景:零售货架识别中商品计数品类分类一体化方案 1. 引言:零售货架管理的痛点与新解法 如果你经营过一家便利店、超市,或者负责过零售门店的运营,一定对“货架盘点”这件事深有感触。每到月底或者需要补货时ÿ…...
Ant Design X:AI赋能前端开发的革命性工具
1. Ant Design X:当设计系统遇上AI会发生什么? 第一次听说Ant Design X时,我正在为一个电商项目焦头烂额地调试聊天机器人组件。传统方案需要自己对接NLP服务、处理对话状态、设计交互逻辑...直到同事扔给我一个链接:"试试这…...
Ostrakon-VL 模型推理加速实战:使用 .accelerate 库优化扫描速度
Ostrakon-VL 模型推理加速实战:使用 .accelerate 库优化扫描速度 1. 效果惊艳的开场 最近在测试Ostrakon-VL模型时,我发现了一个令人惊喜的事实:通过.accelerate库的几项简单优化,模型推理速度可以提升3倍以上,同时显…...

