指针详解之 多层嵌套的关系

news2025/2/8 23:49:56

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 开始的四个字节中:
    • 0x10000x12(最低有效字节)
    • 0x10010x05
    • 0x10020x00
    • 0x10030x00(最高有效字节)
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。如果不了解这一点,可能会导致跨平台移植时出现错误


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2266903.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

内置ALC的前置放大器D2538A/D3308

一、概述 D2538A/D3308是芯谷科技推出的带有ALC&#xff08;自动电平控制&#xff09;的前置音频放大器芯片&#xff0c;最初产品为单声道/立体声收录机及盒式录音机而开发&#xff0c;作为录音/回放的磁头放大器使用&#xff1b;由于产品的高增益、低噪声及ALC外部可调的特性&…

重发布和路由策略实验

需求&#xff1a;将1.1.1.0/24网段重发布到网络中&#xff0c;不允许出现次优路径&#xff0c;实现全网可达。1、在R1上重发布1.1.1.0/24网段&#xff0c;但是需要过滤192.168.12.0/24和192.168.13.0/242、在R2和R3上执行双向重发布因为R1引入的域外路由信息的优先级为150&…

低资源场景下的知识抽取

Information Extraction in Low-Resource Scenarios: Survey and Perspective Low-Resource & IE 技术解决方案 传统 1. 数据增强 概念&#xff1a;主要利用内源或外源辅助资源对原始小数据集进行数据增强或知识增强目标&#xff1a;创建更具代表性的样本并利用更高资源…

Pico “版权校验不通过:签名非法” 处理方法?

遇到 ”版权校验不通过“ 问题时&#xff0c;参考以下思路进行排查。 若应用的 APK 文件未上传至 PICO 开发者管理平台&#xff0c;参考以下排查思路&#xff1a; 检查应用包名&#xff0c;如果使用了 Unity 模板工程默认包名&#xff0c;比如 com.UnityTechnologies.com.uni…

去除 el-input 输入框的边框(element-ui@2.15.13)

dgqdgqdeMac-mini spid-admin % yarn list --pattern element-ui yarn list v1.22.22 └─ element-ui2.15.13 ✨ Done in 0.23s.dgqdgqdeMac-mini spid-admin % yarn list vue yarn list v1.22.22 warning Filtering by arguments is deprecated. Please use the pattern opt…

RCE-PLUS (学习记录)

源码 <?php error_reporting(0); highlight_file(__FILE__); function strCheck($cmd) {if(!preg_match("/\;|\&|\\$|\x09|\x26|more|less|head|sort|tail|sed|cut|awk|strings|od|php|ping|flag/i", $cmd)){return($cmd);}else{die("i hate this"…

macrodroid通过http请求控制手机运行宏

macrodroid adb命令 adb shell pm grant com.arlosoft.macrodroid android.permission.WRITE_SECURE_SETTINGS例:http请求手机播放指定MP3文件 声音素材_电量过低提醒 新建一个宏 添加触发器-连接-http服务器请求 路径随意填,最好不要有特殊符号,不然浏览器识别链接会出错,…

产品初探Devops!以及AI如何赋能Devops?

DevOps源自Development&#xff08;开发&#xff09;和Operations&#xff08;运维&#xff09;的组合&#xff0c;是一种新的软件工程理念&#xff0c;旨在打破传统软件工程方法中“开发->测试->运维”的割裂模式&#xff0c;强调端到端高效一致的交付流程&#xff0c;实…

再谈c++线性关系求值

目的 线性关系是最简单的一种关系&#xff0c;在编程当中应用非常多&#xff0c;所以&#xff0c;再说一次线性关系。 线性关系的定义是这样的&#xff1a; 两个变量之间存在一次方函数关系&#xff0c;就称它们之间存在线性关系。正比例关系是线性关系中的特例&#xff0c;反…

“事务认证平台”:个人日常事务管理系统的诚信体系建设

3.1系统体系结构 系统的体系结构非常重要&#xff0c;往往决定了系统的质量和生命周期。针对不同的系统可以采用不同的系统体系结构。本系统为个人日常事务管理系统&#xff0c;属于开放式的平台&#xff0c;所以在体系结构中采用B/s。B/s结构抛弃了固定客户端要求&#xff0c;…

无人零售 4G 工业无线路由器赋能自助贩卖机高效运营

工业4G路由器为运营商赋予 “千里眼”&#xff0c;实现对贩卖机销售、库存、设备状态的远程精准监控&#xff0c;便于及时补货与维护&#xff1b;凭借强大的数据实时传输&#xff0c;助力深度洞察销售趋势、优化库存、挖掘商机&#xff1b;还能远程升级、保障交易安全、快速处理…

HTML制作一个普通的背景换肤案例2024版

一&#xff0c;完整的代码&#xff1a; <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>换肤</t…

学习threejs,PerspectiveCamera透视相机和OrthographicCamera正交相机对比

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.PerspectiveCamera透…

apifox

请求头head 如果传json串的话&#xff0c;需要将Content-Type覆盖为application/json 请求体body 有一个场景&#xff1a;我先创建任务&#xff0c;返回值为任务id&#xff0c;接着我要去根据任务id 删除这个任务 如果创建任务api的返回值中&#xff0c;任务id是以数组/ 对象…

虚幻引擎结构之ULevel

在虚幻引擎中&#xff0c;场景的组织和管理是通过子关卡&#xff08;Sublevel&#xff09;来实现的。这种设计不仅提高了资源管理的灵活性&#xff0c;还优化了游戏性能&#xff0c;特别是在处理大型复杂场景时。 1. 场景划分模式 虚幻引擎采用基于子关卡的场景划分模式。每个…

基于SpringBoot在线音乐系统平台功能实现十七

一、前言介绍&#xff1a; 1.1 项目摘要 随着互联网技术的迅猛发展和普及&#xff0c;人们对音乐的获取和欣赏方式发生了巨大改变。传统的音乐播放方式&#xff0c;如CD、磁带或本地下载的音乐文件&#xff0c;已经不能满足用户日益增长的需求。用户更希望通过网络直接获取各…

圣诞树的装饰挑战:非线性分析能阻止圣诞树倒塌吗?

节日带来了独特的机会让我们看到工程原理可以在意想不到的地方大展身手&#xff0c;比如装饰圣诞树。 实际上&#xff0c;Altair 技术专家 Matthew Sauer 在装饰他家的圣诞树时就遇到了这样的机会。从一个节日传统开始&#xff0c;很快就变成了一个引人入胜的非线性分析案例研究…

线性代数行列式

目录 二阶与三阶行列式 二元线性方程组与二阶行列式 三阶行列式 全排列和对换 排列及其逆序数 对换 n阶行列式的定义 行列式的性质 二阶与三阶行列式 二元线性方程组与二阶行列式 若是采用消元法解x1、x2的话则得到以下式子 有二阶行列式的规律可得&#xff1a;分…

京东零售数据可视化平台产品实践与思考

导读 本次分享题目为京东零售数据可视化平台产品实践与思考。 主要包括以下四个部分&#xff1a; 1. 平台产品能力介绍 2. 业务赋能案例分享 3. 平台建设挑战与展望 作者&#xff1a;梁臣 京东 数据产品架构师 01平台产品能力介绍 1. 产品矩阵 数据可视化产品是一种利用…

对外发PDF设置打开次数

在线 Host PDF 文件并对链接进行限制——保障文件安全的最佳解决方案 在数字化办公和远程协作日益普及的今天&#xff0c;如何安全高效地分享 PDF 文件成为许多用户关注的重点。MaiPDF 作为一款功能强大的在线工具&#xff0c;不仅支持在线 host PDF 文件&#xff0c;还提供多…