C语言 深入理解指针

目录

前言

指针的重要概念

剖析 

题目一

题目二

题目三

题目四

题目五

题目六

题目七

题目八

**++cpp 

*--*++cpp + 3 

*cpp[-2] + 3 

cpp[-1][-1] + 1 


 

前言

简单来说,指针是一个变量,其值为另一个变量的地址。通过指针,我们可以直接访问、修改其他变量的值,并且可以动态地进行内存管理和数据操作。

指针的重要概念

  1. 指针变量
    指针变量是用来存储内存地址的变量。通过指针,我们可以直接访问和操纵其他变量所在的内存地址。

  2. 内存地址
    在计算机中,每个变量都存储在内存中的某个位置,每个位置都有一个唯一的地址。指针存储的就是这个地址。

  3. 指针的声明和使用
    在C和C++等语言中,指针的声明使用*来表示。例如,int *ptr; 表示声明了一个指向整数的指针变量。指针的使用一般包括取地址操作&和解引用操作*。取地址操作用于获取变量的地址,解引用操作用于访问指针所指向的地址中存储的值。

  4. 指针和数组
    数组名本身就是数组第一个元素的地址。因此,指针和数组紧密相关,可以通过指针来遍历数组。此外,可以通过指针进行数组元素的动态分配和释放。

  5. 指针和函数
    函数参数可以是指针,通过指针参数,可以实现对函数外部变量的修改,或者动态传递数据。另外,可以通过指针返回动态分配的内存。

  6. 指针和动态内存分配
    通过指针,我们可以使用 malloc()calloc() 和 realloc() 等函数在堆上动态分配内存。同时,需要负责及时释放动态分配的内存,以防止内存泄漏。

  7. 指针和指针算术
    指针的值是内存地址,因此可以进行指针加法和指针减法。这在涉及数组、字符串和内存操作时非常有用。

  8. 指针的安全性
    使用指针需要特别注意指针的安全性,比如空指针、野指针、指针溢出等问题都可能导致程序崩溃或不可预测的行为。

剖析 

题目一

&a取的是整个数组的地址,他的类型是int(*)[5],权限是20个字节,强制类型转换为 int*后,它的权限变为了4个字节即一个整形。不强制类型转换,ptr操作时也只会访问4个字节,取决于自己的指针类型,而不取决于所赋给自己的类型,但是会报警告

    int a[5] = {1, 2, 3, 4, 5};
    int *ptr = (int *)(&a + 1);
    printf("%d,%d\n", *(a + 1), *(ptr - 1)); // 2 5

题目二

    struct Test
    {
        int Num;
        char *pcName;
        short sDate;
        char cha[2];
        short sBa[4];
    } *p = (struct Test *)0x100000;
    // 假设p 的值为0x100000。 如下表表达式的值分别为多少?
    // 已知,结构体Test类型的变量大小是20个字节

    /* 0x100000 加上20字节后--> 0x100014 */
    printf("%p\n", p + 0x1);
    /* 1048,576 +1 --> 1028,577 */
    printf("%p\n", (unsigned long)p + 0x1);
    /* 0x100000 + 4 --> 0x100004 */
    printf("%p\n", (unsigned int *)p + 0x1);

题目三

    int a[4] = {1, 2, 3, 4};
    int *ptr1 = (int *)(&a + 1);
    int *ptr2 = (int *)((int)a + 1);
    printf("%x,%x\n", ptr1[-1], *ptr2); // 4 2000000

题目四

初始化二维数组a时,用的是(),并不是{},这里是逗号表达式,最右边为准,因此实际初始化的a是 a[3][2] = {1, 3, 5, 0, 0, 0}

a[0]是第一行的数组名,表示首元素的地址,即p == a[0][0]的地址


    int a[3][2] = {(0, 1), (2, 3), (4, 5)}; 
    int *p;
    p = a[0];            
    printf("%d\n", p[0]); // 1   p[0] == *(arr + 0)

题目五

%d形式输出 &p[4][2] - &a[4][2]

int(*p)[4] ——> p是一个指向有4个整形的数组的指针

p = a;

        p的类型是 int (*) [4]

        a的类型是 int (*) [5]

        等号两边类型不相同,编译器会报警告,有下

        a一次访问5个整形,p按照自己的权限进行访问,+1一次访问4个整形

则 p[4][2] ——> * ( * ( p + 4 ) + 2 ) 取地址最终指向 第4行第3列的地址 (蓝色位置)

则 a[4][2] 再取地址最终指向 第5行第2列的地址(红色位置)

两个地址相减 得到的是两个指针之间的元素个数

再有地址由低到高,低地址减去高地址,得到负数

最终&d形式输出 &p[4][2] - &a[4][2] = -4

%p形式输出 &p[4][2] - &a[4][2]

10000000000000000000000000000100 -4的源码

111111111111111111111111111111111011 -4的反码

111111111111111111111111111111111100 -4的补码

再有 %p打印会把补码看作为地址

FF FF FF FC                                           -4的补码作为地址输出

    int a[5][5];
    int(*p)[4];
    p = a;
    printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); // fffffffffffffffc -4

题目六

    int aa[2][5] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int *ptr1 = (int *)(&aa + 1);              // 跳过整个aa数组
    int *ptr2 = (int *)(*(aa + 1));            // 得到第二行的数组名
    printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1)); // 10 5

题目七

char **pa = a 

        将指针数组 a 的首元素的地址赋值给了指针 pa。

        由于数组的元素是指针类型,因此需要使用指向指针的指针 char ** 类型来接收首元素的地址。

        就比如 int a = 1; int *p = &a; p指针指向a的地址,所指的是int类型,那么就需要int修饰p,*代表p是个指针

pa++

        pa++ 将指针 pa 自增,指向了指针数组的下一个元素的地址。因此,pa 指针现在指向了数组 a 的第二个元素地址的地址。 

%s 格式符,它会根据指针指向的地址找到对应的字符串,并将该字符串打印出来。 

    char *a[] = {"work", "at", "alibaba"};
    char **pa = a;
    pa++;
    printf("%s\n", *pa); // at

题目八

char **cp[] = {c + 3, c + 2, c + 1, c};

       cp 是一个二级指针数组。如下,cp 的类型是 char **cp[],这表示它是一个指针的指针数组。每个 cp 数组的元素都是一个 char ** 类型的指针(也就是二级指针)。这意味着每个元素本身是一个指向指针的指针。在这种情况下,每个元素存储了 c 数组中对应字符串指针的地址。

char ***cpp = cp;

        cpp指针变量存放cp二级指针首元素的地址,而首元素 c+3 的地址,所以cpp是c+3的指针的指针cpp 存放的是 cp 的地址。而 cp 本身是一个指向指针的指针数组的首元素的地址。

在代码片段中,cp 的首元素是 c + 3 的地址,因此可以说 cpp 存放着 c + 3 的地址。

**++cpp 

*--*++cpp + 3 

*cpp[-2] + 3 

 

cpp[-1][-1] + 1 

    char *c[] = {"ENTER", "NEW", "POINT", "FIRST"};
    char **cp[] = {c + 3, c + 2, c + 1, c};
    char ***cpp = cp;
    printf("%s\n", **++cpp);         // POINT
    printf("%s\n", *--*++cpp + 3);   // ER *的优先级大于+ ++的优先级大于*
    printf("%s\n", *cpp[-2] + 3);    // ST cpp[-2] ——> *(cpp - 2) 最终——> *(*(cpp - 2)) + 3
    printf("%s\n", cpp[-1][-1] + 1); // EW *(*(cpp - 1) - 1) + 3

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

交易机器人-规则部分

微信公众号:大数据高性能计算 背景 背景是基于人工去做交易本身无法做到24小时无时无刻的交易,主要是虚拟币本身它是24小时交易,人无法做到24小时盯盘,其次就是如果你希望通过配置更加复杂的规则甚至需要爬取最新的信息走模型进行…

二阶低通滤波器(二阶巴特沃斯滤波器)

连续传递函数G(s) 离散传递函数G(z) 差分方程形式 二阶巴特沃斯滤波器参数设计 设计采样频率100Hz,截止频率33Hz。 注意:设计参数使用在离散系统中! 同理,其他不同阶数不同类型的滤波器设计,如二阶高通滤波器、二阶…

OFDM通信系统仿真之交织技术

文章目录 前言一、交织1、概念2、图形举例3、交织的位置 二、MATLAB仿真1、MATLAB 程序2、仿真结果 前言 之前的博客:OFDM深入学习及MATLAB仿真 中有对交织的概念进行讲解,但讲解还是比较浅显,且仿真实现时并没有加入交织及解交织流程&#…

系列十二、强引用、软引用、弱引用、虚引用分别是什么?

一、整体架构 二、强引用(默认支持模式) 2.1、概述 当内存不足时,JVM开始垃圾回收,对于强引用的对象,就算是出现了OOM也不会对该对象进行回收,死都不收。 强引用是我们最常见的普通对象引用,只…

特效!视频里的特效在哪制作——Adobe After Effects

今天,我们来谈谈一款在Adobe系列中推出的一款图形视频处理软件,适用于从事设计和视频特技的机构,包括电视台、动画制作公司、个人后期制作工作室以及多媒体工作室的属于层类型后期软件——Adobe After Effects。 Adobe After Effects&#xf…

苍穹外卖项目笔记(3)——员工管理

前言 这些功能都没有展示对应的测试结果,可自行通过接口文档进行测试,也可以进行前后端联调测试,附代码链接:take-out 1新增员工 1.1 需求分析和设计 产品原型 接口设计 【注】code:操作成功返回1,否则…

Azure Machine Learning - Azure AI 搜索中的集成数据分块和嵌入

在基于索引器的索引编制中,Azure AI _集成矢量化_将数据分块和文本到矢量嵌入添加到技能中,它还为查询添加文本到矢量的转换。 关注TechLead,分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验,同济本…

我叫:选择排序【JAVA】

1.我是个啥子?? 选择式排序:属于内部排序法,从欲排序的数据中,按指定的规则选出某一元素,再依规定交换位置后达到排序的目的。 2.我的思想 基本思想:第一次从arr[0]~arr[n-1]中选取最小值,与arr[0]交换,第…

io多路复用:select、poll和epoll

1、为什么使用多路复用: 1.1单线程BIO监听socket 多路复用一般用于网络io当中,提到网络io我们肯定能想到socket。如果我们想要一个线程单纯的用向下文的方式监听很多个socket看他是否有事件发生,那这样是不可行。 但上一个socket1没有可读事…

哪个电脑录屏软件好用又免费?十大好用的免费录屏软件排行

屏幕录制软件是一个非常有用的辅助工具,可以帮助您录制屏幕上的所有内容,并根据需要将其转换为视频。 此外,免费的屏幕录像机可以为您节省大量不必要的费用。在这篇文章中,我将列出我在工作和学习中最有效使用的 10 个软件。 每…

OSG加载模型时显示读取进度

目录 1. 前言 2. 开发环境说明 3. 功能实现 3.1. 方法1 3.2. 方法2 3.3. 方法3 4. 附加说明 1. 前言 OSG中加载模型文件到视景器,一般通过osgDB::readXXXX系列开头的函数来加载模型,如:osgDB::readNodeFile、osgDB::readImageFile、os…

Pytest自动化测试框架:mark用法---测试用例分组执行

pytest中的mark: mark主要用于在测试用例/测试类中给用例打标记(只能使用已注册的标记名),实现测试分组功能,并能和其它插件配合设置测试方法执行顺序等。 如下图,现在需要只执行红色部分的测试方法,其它方法不执行&am…

【精选】项目管理工具——Maven详解

Maven简介 Maven是一个项目管理工具。它可以帮助程序员构建工程,管理jar包,编译代码,完成测试,项目打包等等。 Maven工具是基于POM(Project Object Model,项目对象模型)实现的。在Maven的管理下…

【React】React 基础

1. 搭建环境 npx create-react-app react-basic-demo2. 基本使用 JSX 中使用 {} 识别 JavaScript 中的表达式,比如变量、函数调用、方法调用等。 if、switch、变量声明等属于语句,不是表达式。 列表渲染使用 map 。 事件绑定用;on 事件名称…

公寓水电管理系统

springbootmybatisthymeleaf 这次练习是尝试将layer与系统结合起来,将新增、修改、删除都和弹窗结合起来。 一、需求分析 二、数据库 三、模块 1、登录页面 哈哈哈,之前做的登录页面都好丑,这是目前做的最好看的一次了。 超级管理员&…

Java 教育局民办教育信息服务与监管平台

1) 项目背景 按照《中华人民共和国民办教育促进法》和《中华人民共和国政府信息公开条例》的相关规定,为满足学生和家长、社会各界获取权威信息的需求,着力解决服务老百姓最后一公里问题,达到宣传民办教育和引导家长择校的效果&#xff0…

Java实现图书管理系统

今天与大家分享的是一个图书管理系统,这里我们运用的是java基础的语法其中包括类和对象、继承、封装、多态、抽象类、接口还有数组等。 我们需要实现一个可以进行管理员操作和用户操作的图书管理系统,其中包括了管理员操作(查找,添加&#x…

SpringBoot中日志的使用log4j

SpringBoot中日志的使用log4j 项目中日志系统是必不可少的,目前比较流行的日志框架有 log4j、logback 等,这两个框架的作者是同一个 人,Logback 旨在作为流行的 log4j 项目的后续版本,从而恢复 log4j 离开的位置。 另外 slf4j(…

定点整数、小数

文章目录 一、定点整数二、定点小数三、定点小数的加/减运算 一、定点整数 二、定点小数 三、定点小数的加/减运算 对两个定点小数A、B进行加法/减法时,需要先转换为补码 计算机硬件如何做定点小数补码的加法:从最低位开始,按位相加&#x…

栈与队列:设计循环队列

目录 题目🔥: 数据模型: 本题大意: 思路分析: 代码分析: 一、定义队列 二、初始化、判断队列的空和满⭐ 初始化: 空满的判断: 三、入队和出队🎇 入队&…