指针的奥秘(二):指针与数组的联系+字符指针+二级指针+指针数组+《剑指offer》笔试题

指针

  • 一.指针与数组的联系
    • 1.数组名的理解
    • 2.使用指针访问数组
    • 3.一维数组传参的本质
  • 二.字符指针
    • 1.字符指针隐藏秘密
    • 2.常量字符串
    • 3.《剑指offer》笔试题
  • 三.二级指针
  • 四.指针数组
    • 1.指针数组模拟二维数组

一.指针与数组的联系

1.数组名的理解

  也许大部分人认为数组名就是一个名称,没有什么特殊意义,但是这么想的话你就错了,其实数组名有着重要的意义——数组首元素的地址
  你若不信的话可以测试一下:

#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("&arr[0] = %p\n", &arr[0]);printf("arr     = %p\n", arr);return 0;
}

在这里插入图片描述
  我们发现数组名和数组首元素的地址打印出的结果⼀模⼀样,数组名就是数组首元素(第⼀个元素)的地址。

#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("%d\n", sizeof(arr));return 0;
}

在这里插入图片描述

  思考:既然数组名是数组首元素的地址,那么输出的不应该是指针变量的大小(4/8)吗?为什么输出的结果又是40呢?

结论:

  • 其实数组名就是数组首元素(第⼀个元素)的地址是对的,但是有两个例外:

  • sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表示整个数组,计算的是整个数组的大小,单位是字节。

  • &数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素的地址虽然数值一样,但还是有区别的)

  • 除此之外,任何地方使用数组名,数组名都表示首元素的地址。

  再度思考:那么数组的地址,数组名与数组首元素的地址这三种又存在什么关系呢?以下的代码将使你清晰理解这三者的联系:

#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("&arr[0]   = %p\n", &arr[0]);printf("&arr[0]+1 = %p\n", &arr[0] + 1);printf("arr       = %p\n", arr);printf("arr+1     = %p\n", arr + 1);printf("&arr      = %p\n", &arr);printf("&arr+1    = %p\n", &arr + 1);return 0;
}

在这里插入图片描述
  数组的地址,数组名与数组首元素的地址打印的结果都是相同的,这是为什么呢?

结论:

  • &arr[0]和&arr[0]+1相差4个字节,arr和arr+1相差4个字节,是因为&arr[0]和arr都是首元素的地址,+1就是跳过⼀个元素,也就是4个字节,而每个字节都有对应的地址,且地址相差1,所以它们的地址就相差4。
  • &arr和&arr+1相差40个字节,这就是因为&arr是数组的地址,+1操作是跳过整个数组,就是40个字节,地址相差(0x26),到这里大家应该搞清楚数组名的意义了吧。

2.使用指针访问数组

  有了前面知识的支持,再结合数组的特点,我们就可以很方便的使用指针访问数组元素了,如下代码:

#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int i = 0;int* p = &arr[0];//等价于int* p = arr;for (i = 0; i < 10; i++){printf("%d ", *(p + i));//等价于*(arr + i)//等价于arr[i],p[i]}return 0;
}

在这里插入图片描述

  • 将*(p+i)换成p[i]也是能够正常打印的,所以本质上p[i]是等价于*(p+i)。
  • 同理arr[i]应该等价于*(arr+i),数组元素的访问在编译器处理的时候,也是转换成首元素的地址+偏移量求出元素的地址,然后解引用来访问的。
  • 还可以这么写*(i+arr),以及这么写i[arr],是不是很奇妙啊,了解一下就行了,不推荐这么写。

  思考:为什么可以使用指针来访问数组呢?
总结:

  • 数组在内存是一块连续的空间,存放的是相同类型的元素。
  • 指针变量是一个变量,是存放地址的变量,数组和指针不是一回事,但是可以利用指针来访问数组,指针进行不断地+1,解引用可以很方便地遍历数组,取出数组的内容。

3.一维数组传参的本质

  首先从⼀个问题开始,我们之前都是在函数外部计算数组的元素个数,那我们可以把数组传给⼀个函数后,函数内部求数组的元素个数吗?如下:

#include <stdio.h>
void test(int arr[])
{int sz2 = sizeof(arr) / sizeof(arr[0]);printf("sz2 = %d\n", sz2);
}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz1 = sizeof(arr) / sizeof(arr[0]);printf("sz1 = %d\n", sz1);test(arr);return 0;
}

在这里插入图片描述
  我们发现在函数内部是没有正确获得数组的元素个数,这又是为什么呢?你也许会想,指针怎么这么…(此处省略一万字),要尝试先接受它,以后学习多了自然都解释地清了。

  • 这就要学习数组传参的本质了,上个小节我们学习了:数组名是数组首元素的地址;那么在数组传参的时候,传递的是数组名,也就是说本质上数组传参传递的是数组首元素的地址。所以函数形参的部分理论上应该使用指针变量来接收首元素的地址。
  • 那么在函数内部我们写sizeof(arr) 计算的是⼀个地址的大小(单位字节)而不是数组的大小(单位字节)。正是因为函数的参数部分是本质是指针,所以在函数内部是没办法求的数组元素个数的。
  • 那形参为什么可以写成数组的形式呢?这是因为C语言考虑到了学者的感受,在学习数组的时候,如果一来就传地址,形参用指针变量来接收,学者会非常地疑惑的。所以说C语言并不是这么冷若冰霜的。

总结:⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。

二.字符指针

1.字符指针隐藏秘密

  在指针的类型中我们知道有⼀种指针类型为字符指针 char* ,存放的是字符的地址,比如:

#include<stdio.h>
int main()
{char ch = 'w';char* pc = &ch;*pc = 'w';return 0;
}

  还有⼀种使用方式如下:

#include<stdio.h>
int main()
{const char* pstr = "hello bit.";//这里是把一个字符串放到pstr指针变量里了吗?printf("%s\n", pstr);return 0;
}

  代码 const char* pstr = “hello bit.”;特别容易让同学以为是把字符串 hello bit 放到字符指针 pstr 里了,但是不妨考虑一下指针存放的是地址,怎么可能会存放字符串呢?其实本质是把常量字符串 hello bit. 首字符(h)的地址放到了指针变量pstr中

2.常量字符串

  常量字符串顾名思义,该字符串不能被修改,接下来看一个代码:

#include<stdio.h>
int main()
{char arr[] = "abcdef";char* p1 = arr;*p1 = 'b';printf("%s\n", arr);char* p2= "abcdef";*p2 = 'b';printf("%s\n", p2);return 0;
}

在这里插入图片描述
可以发现指针p1指向的空间可以修改,而修改指针p2指向的空间则报错:写入访问权限冲突。这是因为p2是常量字符串,它还有更重要的特点,接下来带我慢慢为你分析一二,请看以下的笔试题。

3.《剑指offer》笔试题

  《剑指offer》:中收录了⼀道和字符串相关的笔试题,我们⼀起来学习⼀下:

#include <stdio.h>
int main()
{char str1[] = "hello bit.";char str2[] = "hello bit.";const char* str3 = "hello bit.";const char* str4 = "hello bit.";if (str1 == str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if (str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}

在这里插入图片描述
  你做对了吗?不管对错,看了这张图就都明白了。
在这里插入图片描述
  这里str3和str4指向的是⼀个同⼀个常量字符串。C/C++会把常量字符串存储到单独的⼀个内存区域(只读数据区),当几个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。

总结:

  • str1和str2是两个数组,数组的操作方式是将右边常量字符串的内容拷贝进来,所以他们是两个空间,只是内容相同,所以str1 != str2。
  • 而str3和str4是两个指针, 编译器在处理的时候,会将相同的常量字符串做成同一个地址,所以,str3和str4指向的是同一个常量字符串,所以str3 == str4。

三.二级指针

  思考:指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?这就是二级指针,如下代码:

#include <stdio.h>
int main()
{int a = 10;int* pa = &a;int** ppa = &pa;*pa = 20;printf("%d\n", a);//20**ppa = 30;printf("%d\n", a);//30return 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; 

四.指针数组

  思考:指针数组是指针还是数组?

  • 我们类比⼀下,整型数组,是存放整型的数组,字符数组是存放字符的数组。
  • 那指针数组呢?是存放指针的数组,指针数组的每个元素都是用来存放地址(指针)的。
  • 指针数组的每个元素是地址,又可以指向一块区域。

1.指针数组模拟二维数组

#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*的,就可以存放在parr数组中 int* parr[3] = { arr1, arr2, arr3 };int i = 0;int j = 0;for (i = 0; i < 3; i++){for (j = 0; j < 5; j++){printf("%d ", parr[i][j]);//等价于*(*(parr+i)+j)}printf("\n");}
}

在这里插入图片描述
parr是数组名,表示首元素的地址,也就是数组的地址,这就牵扯到了数组指针,数组指针又是什么呢?
预知后续如何,且听下回分解,指针的奥秘(三)即将来袭!!!你准备好了吗???
创作不易,如果能帮到你的话能赏个三连吗?感谢了!!!
在这里插入图片描述

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

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

相关文章

TL-WN826N无线网卡连接电脑蓝屏,提示rtl8188gu.sys

TL-WN826N无线网卡插电脑就蓝屏&#xff0c;提示rtl8188gu.sys 处理方法&#xff1a; 设备管理器中卸载其他的2.0无线网卡程序和功能中卸载网卡驱动TPlink官网下载 TL-WN826N V1.0_1.0.0&#xff08;https://www.tp-link.com.cn/product_572.html?vdownload&#xff09;&…

【Linux】项目自动化构建工具make/makefile

&#x1f389;博主首页&#xff1a; 有趣的中国人 &#x1f389;专栏首页&#xff1a; Linux &#x1f389;其它专栏&#xff1a; C初阶 | C进阶 | 初阶数据结构 小伙伴们大家好&#xff0c;本片文章将会讲解Linux中项目自动化构建工具make/makefile的相关内容。 如果看到最后…

【知识碎片】2024_05_09

本篇记录了关于C语言的一些题目&#xff08;puts&#xff0c;printf函数的返回值&#xff0c;getchar&#xff0c;跳出多重循环&#xff09;&#xff0c;和一道关于位运算的代码&#xff3b;整数转换&#xff3d;。 C语言碎片知识 如下程序的功能是&#xff08; &#xff09; #…

从古代故事中领悟高情商回话

页面 页面代码 <% layout(/layouts/default.html, {title: 故事管理, libs: [dataGrid]}){ %> <div class"main-content"><div class"box box-main"><div class"box-header"><div class"box-title">&l…

FPGA+MCU+SDRAM方案,用于服装厂生产过程中以及设计过程中制作样板,剪裁布料

FPGAMCUSDRAM方案&#xff0c;用于服装厂生产过程中以及设计过程中制作样板&#xff0c;剪裁布料 客户应用&#xff1a;服装厂制衣 主要功能&#xff1a; 1.支持步进电机、直流电机 2.支持同时3轴电机协调工作 3.支持以太网/USB联机控制 4.支持LCD 屏显示状态 5.支持HP11/…

Rust多线程交叉打印+Send Sync特征讲解

导航 Rust多线程交叉打印Send Sync特征讲解 一、Rust多线程交叉打印二、Send Sync 特征讲解 Rust多线程交叉打印Send Sync特征讲解 一、Rust多线程交叉打印 先说背景有两个线程&#xff0c;分别为0号线程和1号线线程两个线程交叉打印共享值&#xff0c;并将共享值1当标志为fa…

制鞋5G智能工厂数字孪生可视化平台,推进行业数字化转型

制鞋5G智能工厂数字孪生可视化平台&#xff0c;推进行业数字化转型。随着科技的飞速发展&#xff0c;5G技术与智能制造的结合正成为推动制鞋行业数字化转型的重要力量。制鞋5G智能工厂数字孪生可视化平台&#xff0c;不仅提高了生产效率&#xff0c;还优化了资源配置&#xff0…

2024.5.9 作业 xyt

1. 创建一对父子进程&#xff1a; 父进程负责向文件中写入 长方形的长和宽 子进程负责读取文件中的长宽信息后&#xff0c;计算长方形的面积 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <…

从0开始linux(2)——热键、如何查阅手册

欢迎来到博主的专栏——从0开始linux 博主ID&#xff1a;代码小豪 博主使用的linux发行版&#xff1a;CentOS 7.6 文章目录 热键[Tab]热键Ctrl cshift[pageup]和[pagedown] 查阅手册\-\-help在线手册maninfo手册 热键 我们的命令行模式中存在很多的组合热键&#xff0c;这些按…

������森林消防泵:特点及使用场景 /恒峰智慧科技������

在大自然的怀抱中&#xff0c;森林是地球上最美丽的绿色家园。然而&#xff0c;森林火灾却时常威胁着这片美丽的家园。为了保护森林&#xff0c;我们有幸拥有了一支强大的森林消防泵队伍。今天&#xff0c;就让我们一起来了解一下这些守护者们的故事吧&#xff01; &#xfffd…

电脑ip地址设置成什么比较好

随着信息技术的快速发展&#xff0c;IP地址已成为电脑在网络世界中的“身份证”。它不仅是电脑在网络中进行通信的基础&#xff0c;也直接关系到网络连接的稳定性、安全性和效率。然而&#xff0c;面对众多IP地址设置选项&#xff0c;许多用户可能会感到困惑。那么&#xff0c;…

JavaWeb中的Session和Cookie

前言 什么是会话跟踪技术 Cookie 1.什么是cookie 2.Cookie的应用 2.1 保持用户登录状态 2.2 记录用户名 3. Cookie的设置和获取 3.1 、通过HttpServletResponse.addCookie的方式设置Cookie 3.2、浏览器中查看cookie的内容 3.3、服务端获取客户端携带的cookie&#xf…

华为eNSP学习—IP编址

IP编址 IP编址子网划分例题展示第一步:机房1的子网划分第二步:机房2的子网划分第三步:机房3的子网划分IP编址 明确:IPv4地址长度32bit,点分十进制的形式 ip地址构成=网络位+主机位 子网掩码区分网络位和主机位 学此篇基础: ①学会十进制与二进制转换 ②学会区分网络位和…

FPGA+海思ARM方案,可同时接收HDMI/VGA 两种信号,远程控制

FPGA海思ARM方案&#xff0c;可同时接收HDMI/VGA 两种信号&#xff0c;通过配置输出任一图像或者拼接后的图像 客户应用&#xff1a;无线远程控制 主要特性&#xff1a; 1.支持2K以下任意分辨率格式 2.支持H264压缩图像 3.支持WIFI/4G无线传输 4.支持自适应输入图像分辨率 …

光端机(2)——光纤通信学习笔记九

学习笔记里面只关注基本原理和概念&#xff0c;复杂的公式和推导都没有涉及 光端机 光发射机 作用&#xff1a;实现电光转换。将来自电端机的电信号对光源发出的光波进行调制&#xff0c;然后将调制好的光信号耦合到光线中传输。 基本性能要求 1.合适的发光波长&#xff08;光…

GDAL:Warning 1: All options related to creation ignored in update mode

01 警告说明 首先贴出相关代码&#xff1a; out_file_name Rs_{:4.0f}{:02.0f}.tiff.format(year, month) out_path os.path.join(out_dir, out_file_name) mem_driver gdal.GetDriverByName(MEM) mem_ds mem_driver.Create(, len(lon), len(lat), 1, gdal.GDT_Float32) …

限时优惠||新算法转让(一种基于数学的元启发式算法)新的群智能算法转让,新的元启发式算法转让(独家发售)【仅售1份】

新算法 ||新算法转让、新的元启发式算法转让 ||一种基于数学开发的超隐喻的元启发式算法新算法 限时发售、限量1份 1️⃣完整的封装代码 2️⃣配套完整的灵感及数据 3️⃣测试集&#xff08;3个&#xff09; &#xff08;1&#xff09;cec2017&#xff08;10、30、50和100维&a…

HashMap前世今生

概述 HashMap是我们常用的一种数据结构&#xff0c;他是一个key-value结构。我们来深入了解一下。 1.8之前用的数组加链表 1.8之后用的数组加链表加红黑树&#xff0c;当链表数量大于8时&#xff0c;将链表转为红黑树。当红黑书节点小于6又会转为链表。 浅析HashMap的put()方…

深入理解网络原理5----HTTP协议

文章目录 一、HTTP协议格式二、HTTP请求2.1 URL 基本格式2.2 URL encode2.3 "方法" (method)2.4 认识请求 "报头" (header) 三、HTTP 响应3.1 "状态码" (status code) 四、HTPPS工作过程&#xff08;经典面试题&#xff09; 提示&#xff1a;以下…

【全开源】Java U U跑腿同城跑腿小程序源码快递代取帮买帮送源码小程序+H 5+公众号跑腿系统

特色功能&#xff1a; 智能定位与路线规划&#xff1a;UU跑腿小程序能够利用定位技术&#xff0c;为用户提供附近的跑腿服务&#xff0c;并自动规划最佳路线&#xff0c;提高配送效率。订单管理&#xff1a;包括订单查询、订单状态更新、订单评价等功能&#xff0c;全行业覆盖…