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);         // POINTprintf("%s\n", *--*++cpp + 3);   // ER *的优先级大于+ ++的优先级大于*printf("%s\n", *cpp[-2] + 3);    // ST cpp[-2] ——> *(cpp - 2) 最终——> *(*(cpp - 2)) + 3printf("%s\n", cpp[-1][-1] + 1); // EW *(*(cpp - 1) - 1) + 3

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FPGA实现平衡小车(文末开源!!)

FPGA平衡小车 一. 硬件介绍 底板资源: TB6612电机驱动芯片 * 2 MPU6050陀螺仪 WS2812 RGB彩色灯 * 4 红外接收头 ESP-01S WIFI 核心板 微相 A7_Lite Artix-7 FPGA开发板 电机采用的是平衡小车之家的MG310(GMR编码器)电机。底板上有两个TB6612芯片,可以驱动…

qemu + busybox + 内核实验环境搭建(2023-11)

主要是参考网上的例子,网上的一些例子可能用的busybox 老旧,编译各种问题,以及rootfs hda的方式或者ramfs的方式。可能有些概念还是不清楚,以下是最终完成测试成功的案例。 下载kernel https://cdn.kernel.org/pub/linux/kernel…

python数据可视化

绘制简单的折线图 1.1json数据格式 JSON是一种轻量级的数据交互格式。可以按照JSON指定的格式去组织和封装数据,其本质上是一个带有特定格式的字符串。 主要功能:json就是一种在各个编程语言中流通的数据格式,负责不同编程语言中的数据传递…

23 - 如何优化JVM内存分配?

JVM 调优是一个系统而又复杂的过程,但我们知道,在大多数情况下,我们基本不用去调整 JVM 内存分配,因为一些初始化的参数已经可以保证应用服务正常稳定地工作了。 但所有的调优都是有目标性的,JVM 内存分配调优也一样。…

【计算思维】蓝桥杯STEMA 科技素养考试真题及解析 3

1、下图中,乐乐家的位置用数对(4,3)表示,学校在乐乐家西南方向。下列选项中,学校的位置不可能是 A、(5,4) B、(2,2) C、(2,1) D、&#xff…

第五篇 《随机点名答题系统》——抽点答题详解(类抽奖系统、在线答题系统、线上答题系统、在线点名系统、线上点名系统、在线考试系统、线上考试系统)

目录 1.功能需求 2.界面设计 3.流程设计 4.关键代码 随机点名答题系统(类抽奖系统、在线答题系统、线上答题系统、在线点名系统、线上点名系统、在线考试系统、线上考试系统),是基于php(8.2.11),Java…

上海亚商投顾:三大指数小幅上涨 HBM概念股全天强势

上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。 一.市场情绪 三大指数早盘窄幅震荡,午后集体拉升翻红,黄白二线走势分化,题材热点快速轮…

【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】

👨‍💻博客主页:花无缺 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】🌏题目描述🌏输入格…

Ubuntu20.04 安装微信 【wine方式安装】推荐

安装步骤: 第一步:安装 WineHQ 安装包 先安装wine,根据官网指导安装即可。下载 - WineHQ Wikihttps://wiki.winehq.org/Download_zhcn 如果您之前安装过来自其他仓库的 Wine 安装包,请在尝试安装 WineHQ 安装包之前删除它及依赖它的所有安装包(如:wine-mono、wine-gec…

应用开发平台集成表单设计器系列之3——整体集成思路及表单设计器功能深度了解

背景 平台需要实现自定义表单功能,作为低代码开发的一部分,通过技术预研和技术选型,选择form-create和form-create-designer这两个组件进行集成作为实现方案。通过深入了解和技术验证,确认了组件的功能能满足需求,具备…

Linux性能分析——TOP命令详解

我的圈子: 高级工程师聚集地 我是董哥,高级嵌入式软件开发工程师,从事嵌入式Linux驱动开发和系统开发,曾就职于世界500强公司! 创作理念:专注分享高质量嵌入式文章,让大家读有所得! …

初刷leetcode题目(3)——数据结构与算法

😶‍🌫️😶‍🌫️😶‍🌫️😶‍🌫️Take your time ! 😶‍🌫️😶‍🌫️😶‍🌫️😶‍🌫️…