C 中的指针 - 数组和字符串

0. 为什么是指针和数组?

在C语言中,指针和数组有着非常密切的关系。应该将它们放在一起讨论的原因是,使用数组表示法 arrayName[index]) 可以实现的功能也可以使用指针实现,通常速度更快。

1. 一维数组

让我们看看当我们写的时候会发生什么int myArray[5];

从到 开始创建五个连续的内存块,其中包含垃圾值。每个块的大小为 4 字节。myArray[0]myArray[4]

因此,如果 myArray[0] 的地址是100(例如),则其余块的地址将为104108112116

现在,看看下面的代码 -

int prime[5] = {2,3,5,7,11};
printf("Result using &prime = %d\n",&prime);
printf("Result using prime = %d\n",prime);
printf("Result using &prime[0] = %d\n",&prime[0]);/* Output */
Result using &prime = 6422016
Result using prime = 6422016
Result using &prime[0] = 6422016

那么 、&primeprime&prime[0]都给出相同的地址,对吗?好吧,等待并阅读,因为你会感到惊讶(和困惑)。让我们尝试将&primeprime&prime[0]分别递增 1。

printf("Result using &prime = %d\n",&prime + 1);
printf("Result using prime = %d\n",prime + 1);
printf("Result using &prime[0] = %d\n",&prime[0] + 1);/* Output */
Result using &prime = 6422036
Result using prime = 6422020
Result using &prime[0] = 6422020

等等!为什么&prime + 1结果与其他两个不同?为什么prime + 1&prime[0] + 1仍然相等?让我们来回答这些问题。

1.prime&prime[0],都指向数组的第0个元素prime。因此,数组的名称本身就是指向数组第 0 个元素的指针

这里,两者都指向第一个大小为 4 字节的元素。当您向它们添加 1 时,它们现在指向数组中的第一个元素。因此地址增加了 4。

2.&prime另一方面是指向大小为 5 的数组的指针int。它存储数组的基地址prime[5],等于第一个元素的地址。然而,对其加 1 会导致地址增加 5 x 4 = 20 字节。

简而言之,arrayNameand&arrayName[0]指向第 0 个元素,而&arrayName指向整个数组。

我们可以使用下标变量访问数组元素,如下所示 -

int prime[5] = {2,3,5,7,11};
for( int i = 0; i < 5; i++)
{printf("index = %d, address = %d, value = %d\n", i, &prime[i], prime[i]);
}

我们可以使用指针做同样的事情,它总是比使用下标更快。

int prime[5] = {2,3,5,7,11};
for( int i = 0; i < 5; i++)
{printf("index = %d, address = %d, value = %d\n", i, prime + i, *(prime + i));
}

两种方法都会给出输出 -

index = 0, address = 6422016, value = 2
index = 1, address = 6422020, value = 3
index = 2, address = 6422024, value = 5
index = 3, address = 6422028, value = 7
index = 4, address = 6422032, value = 11

因此,&arrayName[i]arrayName[i]分别与arrayName + i和 *(arrayName + i)相同。

2. 二维数组

二维数组是数组的数组。

int marks[5][3] = { { 98, 76, 89},{ 81, 96, 79},{ 88, 86, 89},{ 97, 94, 99},{ 92, 81, 59}};

这里,marks可以认为是一个包含 5 个元素的数组,每个元素都是包含 3 个整数的一维数组。让我们通过一系列程序来理解不同的下标表达式。

printf("Address of whole 2-D array = %d\n", &marks);
printf("Addition of 1 results in %d\n", &marks +1);/* Output */
Address of whole 2-D array = 6421984
Addition of 1 results in 6422044

与一维数组一样,&marks指向整个二维数组,marks[5][3]。因此,增加 1(= 5 个数组 X 3 个整数,每个数组 X 4 个字节 = 60)会导致增加 60 个字节。

printf("Address of 0th array = %d\n", marks);
printf("Addition of 1 results in %d\n", marks +1);
printf("Address of 0th array =%d\n", &marks[0]);
printf("Addition of 1 results in %d\n", &marks[0] + 1);/* Output */
Address of 0th array = 6421984
Addition of 1 results in 6421996
Address of 0th array = 6421984
Addition of 1 results in 6421996

如果marks是一维数组,marks&marks[0]指向第 0 个元素。对于二维数组,元素现在是一维数组。因此,marks&marks[0]指向第 0 个数组(元素),加 1 指向第 1 个数组。

printf("Address of 0th element of 0th array = %d\n", marks[0]);
printf("Addition of 1 results in %d\n", marks[0] + 1);
printf("Address of 0th element of 1st array = %d\n", marks[1]);
printf("Addition of 1 results in %d\n", marks[1] + 1);/* Output */
Address of 0th element of 0th array = 6421984
Addition of 1 results in 6421988
Address of 0th element of 1st array = 6421996
Addition of 1 results in 6422000

现在,差异来了。对于一维数组,marks[0]将给出第 0 个元素的值。增量 1 将使值增加 1。

但是,在二维数组中,marks[0]指向第 0 个数组的第 0 个元素。同样,marks[1]指向第一个数组的第 0 个元素。增量 1 将指向第一个数组中的第一个元素。

printf("Value of 0th element of 0th array = %d\n", marks[0][0]);
printf("Addition of 1 results in %d", marks[0][0] + 1);/* Output */
Value of 0th element of 0th array = 98
Addition of 1 results in 99

这是新的部分。marks[i][j]给出第 i 个数组的第 j 个元素的值。对其进行增量会更改存储在 处的值marks[i][j]。现在,让我们尝试marks[i][j]用指针来写。

从我们之前的讨论中我们知道marks[i] + j将指向第 j 个数组的第 i 个元素。取消引用它意味着该地址处的值。因此,marks[i][j]等于*(marks[i] + j)

从我们对一维数组的讨论来看,marks[i]与 相同*(marks + i)。因此,marks[i][j]可以写成*(*(marks + i) + j)指针的形式。

以下是比较一维和二维数组的符号摘要。

表达一维数组二维阵列
&ArrayName指向整个数组的地址,加1使地址增加1 x sizeof(ArrayName)指向整个数组的地址,加1使地址增加1 x sizeof(ArrayName)
ArrayName指向 元素,加1增加元素0th的地址1st指向元素(数组),加1增加元素(数组)0th的地址1st
&ArrayName[i]指向元素,加1增加元素ith的地址(i+1)th指向元素(数组),加1增加元素(数组)ith的地址(i+1)th
ArrayName[i]给出元素的值,加 1 增加元素ith的值ith指向数组0th的元素,加1增加数组元素的ith地址1stith
ArrayName[i][j]没有什么jth给出数组元素的值,加 1 会增加数组元素ith的值jthith
访问元素的指针表达式*(ArrayName + i)*(*(ArrayName + i) + j)

3. Strings

字符串是一个以 结尾的一维字符数组null(\0)。当我们写入时char name[] = "Srijan";,每个字符占用一个字节的内存,最后一个字符始终占据内存\0

与我们见过的数组类似,nameand&name[0]指向0th字符串中的字符,而 while 则&name指向整个字符串。又可name[i]写为*(name + i).

/* String */
char champions[] = "Liverpool";printf("Pointer to whole string = %d\n", &champions);
printf("Addition of 1 results in %d\n", &champions + 1);/* Output */
Address of whole string = 6421974
Addition of 1 results in 6421984printf("Pointer to 0th character = %d\n", &champions[0]);
printf("Addition of 1 results in %d\n", &champions[0] + 1);/* Output */
Address of 0th character = 6421974
Addition of 1 results in a pointer to 1st character 6421975printf("Pointer to 0th character = %d\n", champions);
printf("Addition of 1 results in a pointer to 1st character %d\n", champions + 1);/* Output */
Address of 0th character = 6421974
Addition of 1 results in 6421975printf("Value of 4th character = %c\n", champions[4]);
printf("Value of 4th character using pointers = %c\n", *(champions + 4));/* Output */
Value of 4th character = r
Value of 4th character using pointers = r

如前所述,还可以访问和操作二维字符数组或字符串数​​组。

/* Array of Strings */
char top[6][15] = {"Liverpool","Man City","Man United","Chelsea","Leicester","Tottenham"};printf("Pointer to 2-D array = %d\n", &top);
printf("Addition of 1 results in %d\n", &top + 1);/* Output */
Pointer to 2-D array = 6421952
Addition of 1 results in 6422042printf("Pointer to 0th string = %d\n", &top[0]);
printf("Addition of 1 results in %d\n", &top[0] + 1);/* Output */
Pointer to 0th string = 6421952
Addition of 1 results in 6421967printf("Pointer to 0th string = %d\n", top);
printf("Addition of 1 results in %d\n", top + 1);/* Output */
Pointer to 0th string = 6421952
Addition of 1 results in 6421967printf("Pointer to 0th element of 4th string = %d\n", top[4]);
printf("Pointer to 1st element of 4th string = %c\n", top[4] + 1);/* Output */
Pointer to 0th element of 4th string = 6422012
Pointer to 1st element of 4th string = 6422013printf("Value of 1st character in 3rd string = %c\n", top[3][1]);
printf("Same using pointers = %c\n", *(*(top + 3) + 1));/* Output */
Value of 1st character in 3rd string = h
Same using pointers = h

4. 指针数组

与 s 数组int和 s 数组一样char,也存在指针数组。这样的数组只是地址的集合。这些地址也可以指向单个变量或另一个数组。

声明指针数组的语法是 -

dataType *variableName[size];/* Examples */
int *example1[5];
char *example2[8];

按照运算符优先级,第一个示例可以理解为 - example1是一个包含 5 个指向 的指针的 array( [])int。同样,example2是一个由 8 个指针组成的数组char

我们可以使用指针数组将二维数组存储为字符串top,这样也可以节省内存。

char *top[] = {"Liverpool","Man City","Man United","Chelsea","Leicester","Tottenham"};

top将包含所有相应名称的基地址。的基地址"Liverpool"将存储在top[0]"Man City"intop[1]等中。

在之前的声明中,我们需要 90 个字节来存储名称。在这里,我们只需要 ( 58 (名称字节总和) + 12 (在数组中存储地址所需的字节) ) 70 个字节。

使用指针数组时,字符串或整数的操作变得更加容易。

如果我们尝试放在"Leicester"之前,我们只需要切换和"Chelsea"的值,如下所示 -top[3]top[4]

char *temporary;
temporary = top[3];
top[3] = top[4];
top[4] = temporary;

如果没有指针,我们将需要交换字符串的每个字符,这将花费更多时间。这就是为什么字符串通常使用指针声明。

5. 指向数组的指针

就像“指向int”或“指向char”的指针一样,我们也有指向数组的指针。该指针指向整个数组而不是其元素。

还记得我们讨论过如何&arrayName指向整个数组吗?嗯,它是一个指向数组的指针。

指向数组的指针可以这样声明 -

dataType (*variableName)[size];/* Examples */
int (*ptr1)[5];
char (*ptr2)[15];

注意括号。如果没有它们,这些将是一个指针数组。第一个示例可以理解为 -是一个指向 5 (整数)ptr1数组的指针int

int goals[] = { 85,102,66,69,67};
int (*pointerToGoals)[5] = &goals;
printf("Address stored in pointerToGoals %d\n", pointerToGoals);
printf("Dereferncing it, we get %d\n",*pointerToGoals);/* Output */
Address stored in pointerToGoals 6422016
Dereferencing it, we get 6422016

当我们取消引用指针时,它会给出该地址处的值。类似地,通过取消引用指向数组的指针,我们得到该数组,并且该数组的名称指向基地址。如果我们找到数组的大小,我们就可以确认*pointerToGoals给出了数组。goals

printf("Size of goals[5] = %d, *pointerToGoals);/* Output */
Size of goals[5] = 20

如果我们再次取消引用它,我们将获得存储在该地址中的值。我们可以使用 打印所有元素pointerToGoals

for(int i = 0; i < 5; i++)
printf("%d ", *(*pointerToGoals + i));/* Output */
85 102 66 69 67

指针和数组指针与函数配合使用时非常有用。我们将在下一篇中讨论它们。

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

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

相关文章

简谈PostgreSQL的wal_level=logic

一、PostgreSQL的wal_levellogic的简介 wal_levellogic 是 PostgreSQL 中的一个配置选项&#xff0c;用于启用逻辑复制&#xff08;logical replication&#xff09;功能。逻辑复制是一种高级的数据复制技术&#xff0c;它允许您将变更&#xff08;例如插入、更新和删除&#…

Linux系统中进程间通信(Inter-Process Communication, IPC)

文章目录 进程间通信介绍进程间通信目的进程间通信发展 管道什么是管道 匿名管道用fork来共享管道原理站在文件描述符角度-深度理解管道站在内核角度-管道本质管道读写规则管道特点 命名管道创建一个命名管道匿名管道与命名管道的区别命名管道的打开规则 命名管道的删除用命名管…

Shopify二次开发之三:liquid语法学习(访问Objects和Schema数据模型)

目录 Objects &#xff08;对象&#xff09; 全局对象 all_products&#xff1a;商店中所有的商品 articles: 商店中的所有文章 collections&#xff1a;商店中所有的集合 模板对象 在product.json&#xff08;配置的section中) 访问product对象 在collection.json中可…

40. 组合总和 II

题目描述 给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用 一次 。 **注意&#xff1a;**解集不能包含重复的组合。 示例 1: 输入: candidates…

Android之 知识总结第二篇

一&#xff0c; Gradle 、AGP(Android Gradle Plugin)、 buildTools分别是什么&#xff0c;他们之间什么关系&#xff1f; Gradle Gradle是基于JVM的构建工具。他本身使用jave写的&#xff0c;gradle的脚本也就是build.gradle通常是用groovy语言。Android BuildTools Android S…

1D和2D布朗运动matlab

布朗运动是一种随机现象&#xff0c;下面的M函数brwnm2.m给出了二维Brown运动&#xff0c;其中[t0,tf]是时间区间&#xff0c;h是采样步长&#xff0c;w(t)&#xff0c;z(t)是布朗运动。function [t,w,z]brwnm2(t0,tf,h) tt0:h:tf; xrandn(size(t))*sqrt(h); yrandn(size(t))*s…

LightDB - 支持quarter 函数[mysql兼容]

LightDB 从23.4版本开始支持 quarter 函数。 简介 quarter 函数用来确定日期对应的季度&#xff0c; 如 ‘20231204’ 对应12月&#xff0c;也就是第四季度。 下面为mysql8.0中描述 Returns the quarter of the year for date, in the range 1 to 4, or NULL if date is NUL…

二叉树题目:二叉树的完全性检验

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;二叉树的完全性检验 出处&#xff1a;958. 二叉树的完全性检验 难度 5 级 题目描述 要求 给定一个二叉树的根结点 root \texttt{root} root&…

C#中GDI+图形图像技术(Graphics类、Pen类、Brush类)

目录 一、创建Graphics对象 1.创建Pen对象 2.创建Brush对象 &#xff08;1&#xff09;SolidBrush类 &#xff08;2&#xff09;HatchBrush类 ​​​​​​​&#xff08;3&#xff09;LinerGradientBrush类 用户界面上的窗体和控件非常有用&#xff0c;且引人注目&#…

销售人员一定要知道的6种获取电话号码的方法

对于销售来说&#xff0c;电话销售是必须要知道的销售方法&#xff0c;也是销售生涯中的必经之路。最开始我们并不清楚这么电话是从哪里来的&#xff0c;也不清楚是通过哪些方法渠道获取。那么今天就来分享给各位销售人员获取客户电话号码的方法。 1.打印自己的名片&#xff0…

CSS实现一些小功能

1.信封边框的实现 1.1 使用背景渐变 <!DOCTYPE html><html><head><meta charset"UTF-8"><title></title><style type"text/css">.uu {width: 200px;height: 70px;padding:1em;border: 1em solid transparent;…

【objectarx.net】加载线型文件

/// <summary> /// 加载指定线型文件中的指定名称的线型 /// </summary> /// <param name"lineTypName">线型名</param> /// <param name"db"></param> /// <param name"lineTypeFile">线型文件</…

一文读懂 ChatGPT 工作原理

生成式 AI 是如何工作的 生成式 AI 是目前最热门的技术之一&#xff0c;ChatGPT 等应用的出现使人们对于如何在各种领域中应用生成式 AI 有了许多新的思考。 但这些领域的从业者并不都具备 AI 相关的知识背景。所以我们制作了这个教程&#xff0c;向无 AI 知识背景的读者介绍…

Temu数据面板:Temu商家必备的数据分析工具

在Temu这个电商平台上&#xff0c;越来越多的商家意识到数据分析的重要性。数据分析可以帮助商家更好地了解店铺的运营情况&#xff0c;从而制定更有效的运营策略&#xff0c;提高销售业绩。而在这个过程中&#xff0c;Temu数据面板成为了一个不可或缺的工具。 先给大家推荐一款…

【ElementUI】一行代码解决图片预览

【ElementUI】一行代码解决图片预览 只需要在图片标签上加入:preview-src-list 只需要在图片标签上加入:preview-src-list 完整代码如下&#xff1a; <el-table-column label"封面" align"center" prop"cover" :sort-orders"[descend…

IDEA 保存自动ESLint格式化

作为后端人员&#xff0c;偶尔修改一下前端代码&#xff0c;ESLint总提示格式不正确。有没有什么办法实现自动格式化呢&#xff1f; 安装插件Save Actions Tool 设置中搜索eslint 勾选 Run eslint --fix on save 这样以后&#xff0c;只要保存文件就会自动格式化了。 参考 …

“探秘某音开放平台接口:掌握潮流电商的秘密武器“

一、概述 接口是某音开放平台提供的一套API接口&#xff0c;开发者可以通过这些接口快速构建与某音平台的数据交互能力。本文将详细介绍某音接口的使用方法、技术细节以及注意事项。 二、接口地址 某音接口的地址为&#xff1a;https://open.douyin.com/platform/doc/standa…

java语言中受检异常和非受检异常的区别是什么?

在Java语言中&#xff0c;异常可以分为两种类型&#xff1a;受检异常&#xff08;Checked Exception&#xff09;和非受检异常&#xff08;Unchecked Exception&#xff09;。 受检异常&#xff08;Checked Exception&#xff09;&#xff1a;这是编译器要求必须进行处理的异常…

Redis——Redis简介

Redis是目前最流行的键值对&#xff08;key-value&#xff09;数据库&#xff0c;以出色的性能著称&#xff0c;官方提供的数据是可以支持100000以上的QPS。Redis具有高性能的主要原因如下&#xff1a; Redis是基于内存的存储数据库&#xff0c;绝大部分的命令处理只是纯粹的内…

BabySpartan:对non-uniform computation的Lasso-based SNARK

1. 引言 前序博客有&#xff1a; Lasso、Jolt 以及 Lookup Singularity——Part 1Lasso、Jolt 以及 Lookup Singularity——Part 2深入了解LassoJoltmultilinear多项式承诺方案benchmark对比 见Srinath Setty和Justin Thaler 2023年论文《BabySpartan: Lasso-based SNARK fo…