热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【C语言进阶剖析】29、指针和数组分析(下)

文章目录1数组的访问方式1.1两种数组访问方式1.2下标形式和指针形式对比2a和&a的区别3数组参数4小结在开始之前,先思考一个问题:数组名可以当作常量

文章目录

    • 1 数组的访问方式
      • 1.1 两种数组访问方式
      • 1.2 下标形式和指针形式对比
    • 2 a 和 &a 的区别
    • 3 数组参数
    • 4 小结



在开始之前,先思考一个问题:数组名可以当作常量指针使用,那么指针是否也可以当作数组名来使用呢?

1 数组的访问方式


1.1 两种数组访问方式

访问数组中的元素有两种访问方式,通过下标访问和通过指针访问数组
在这里插入图片描述

1.2 下标形式和指针形式对比

下标形式和指针形式基本是等价的,但是效率略有区别

  • 指针以固定增量在数组中移动时,效率略高于下标形式
  • 指针增量为 1 且硬件具有硬件增量模型时,效率更高

注意:现代编译器的生成代码优化率已大大提高,在固定增量时,下标形式的效率已经和指针形式相当;但从可读性和代码维护的角度来看,下标形式更优

下标形式与指针形式之间可以相互转换,转换如下:
在这里插入图片描述
这里 a[n] 可是可以转换为 n[a] 的哦

下面通过一个例子说明数组的指针访问和下标访问

// 29-1.c
#include
int main()
{int a[5] &#61; {0};int* p &#61; a;int i &#61; 0;for (i &#61; 0; i < 5; i&#43;&#43;){p[i] &#61; i &#43; 1;}for (i &#61; 0; i < 5; i&#43;&#43;){printf("a[%d] &#61; %d\n", i, *(a &#43; i));}printf("\n");for (i &#61; 0; i < 5; i&#43;&#43;){i[a] &#61; i &#43; 10;}for (i &#61; 0; i < 5; i&#43;&#43;){printf("p[%d] &#61; %d\n", i, p[i]);}return 0;
}

编译运行结果如下&#xff1a;

$ gcc 29-1.c -o 29-1
$ ./29-1
a[0] &#61; 1
a[1] &#61; 2
a[2] &#61; 3
a[3] &#61; 4
a[4] &#61; 5p[0] &#61; 10
p[1] &#61; 11
p[2] &#61; 12
p[3] &#61; 13
p[4] &#61; 14

从结果我们可以看到&#xff0c;通过指针和数组都可以实现访问数组中的元素&#xff0c;可以把数组名当作常量指针使用&#xff0c;也可以把指针当作数组名来使用&#xff0c;a[i] 和 i[a] 是等效的

指针和数组的概念不是完全相等的&#xff0c;二着是有区别的&#xff0c;我们通过一个例子来说明指针数组和指针的不同

// 29-2.c
#include
int main()
{extern int a[];printf("&a &#61; %p\n", &a);printf("a &#61; %p\n", a);printf("*a &#61; %d\n", *a);return 0;
}

// ext.c
int a[] &#61; {1, 2, 3, 4, 5};

编译运行结果如下&#xff1a;

$ gcc 29-2.c ext.c -o 29-2
$ ./29-2
&a &#61; 0x560167697010
a &#61; 0x560167697010
*a &#61; 1

a 是数组首元素的地址&#xff0c;&a 是整个数组的地址&#xff0c;在数值上二者是相同的&#xff0c;*a 表示取元素第一个元素。

如果把 29-2.c 的第 6 行 extern int a[]; 改为 extern int *a; 代码更改如下&#xff1a;

// 29-2.c
#include
int main()
{extern int *a; // a[] 改为 *aprintf("&a &#61; %p\n", &a);printf("a &#61; %p\n", a);printf("*a &#61; %d\n", *a);return 0;
}

再次编译运行&#xff0c;结果完全不一样了

$ gcc 29-2.c ext.c -o 29-2
$ ./29-2
&a &#61; 0x562f0c0ba010
a &#61; 0x200000001
段错误 (核心已转储)

为什么会出现这个运行结果呢&#xff1f;编译器编译 ext.c 后&#xff0c;数组 a 的地址就是 0x562f0c0ba010&#xff0c;等价于 a 的地址就是 0x562f0c0ba010。编译 29-2.c 时&#xff0c;碰见 extern int* a; 就寻找其他位置定义的 a 的值&#xff0c;a 的地址为 0x562f0c0ba010&#xff0c;所以 &a 就是取 a 的地址&#xff0c;就是 0x562f0c0ba010&#xff0c;a 的值是该地址下对应的数据&#xff0c;为 0x200000001。*a 表示取地址值为 0x200000001 的数据&#xff0c;不合法&#xff0c;是不允许访问的&#xff0c;返回段错误。
下面来说明一下为什么地址 0x562f0c0ba010 对应的数据为 0x200000001&#xff0c;我们打印的是 %p&#xff0c;%p 代表的是&#xff1a;按十六进制输出数据&#xff0c;长度为 8 个字节&#xff0c;Linux 是小端序系统&#xff0c;低地址存放低位&#xff0c;数组存储方式如下&#xff0c;取 8 字节数据为 0x200000001

在这里插入图片描述

2 a 和 &a 的区别


  • a 为数组首元素的地址
  • &a 为整个数组的地址
  • a 和 &a 的区别在于指针运算

在这里插入图片描述
a &#43; 1 表示指向下一个元素&#xff0c;a &#43; 1是越过一个元素&#xff0c;&a &#43; 1 表示指向这个数组最后一个元素后面的位置&#xff0c;&a &#43; 1 是越过一个数组&#xff0c;可以从下图清晰的看到二者的指向
在这里插入图片描述

下面看一个例子&#xff0c;更好的区分 a 和 &a

// 29-3.c
#include
int main()
{int a[5] &#61; {1, 2, 3, 4, 5};int* p1 &#61; (int*)(&a &#43; 1);int* p2 &#61; (int*)((long long)a &#43; 1);int* p3 &#61; (int*)(a &#43; 1);printf("%d, %d, %d\n", p1[-1], p2[0], p3[1]);return 0;
}

$ gcc 29-3.c -o 29-3
$ ./29-3
5, 33554432, 3

  • p1 指向整个数组最后一个元素后面的位置&#xff0c; p1[-1] &#61;&#61;> *(p1 -1)&#xff0c;所以 p1[-1] 指向数组最后一个元素。
  • (int*)((long long)a &#43; 1); 先把指针转换为 long long 再相加&#xff0c;这已经不是指针运算了&#xff0c;就是基本数据类型之间的运算&#xff0c;再次解引用&#xff0c;等于从数组首元素的第二个字节开始&#xff0c;取 4 个字节的数据&#xff0c;结果如下&#xff0c;linux 系统是小端序&#xff0c;取到的数据为 0x02000000&#xff0c;等于十进制的 33554432。
  • p3 指向数组第二个元素

在这里插入图片描述

3 数组参数


  • 数组作为函数参数时&#xff0c;编译器将其编译成对应的指针

在这里插入图片描述
结论&#xff1a;将数组名作为参数传递给函数时&#xff0c;数组名退化为指针&#xff0c;一般情况下&#xff0c;当定义的函数中有数组参数时&#xff0c;需要定义另一个参数来表示数组的大小

下面通过例子来验证我们的理论

// 29-4.c
#include
void func1(char a[5])
{printf("In func1: sizeof(a) &#61; %ld\n", sizeof(a));*a &#61; &#39;a&#39;;a &#61; NULL;
}
void func2(char b[])
{printf("In func2: sizeof(b) &#61; %ld\n", sizeof(b));*b &#61; &#39;b&#39;;b &#61; NULL;
}
int main()
{char array[10] &#61; {0};func1(array);printf("array[0] &#61; %c\n", array[0]);func2(array);printf("array[0] &#61; %c\n", array[0]);return 0;
}

在 func1() 函数中&#xff0c;&#xff0c;打印 sizeof(a)&#xff0c;数组退化成指针&#xff0c;所以不管数组长度如何&#xff0c;打印的都是指针的长度&#xff0c;由于退化成指针&#xff0c;所以允许 修改指针 a &#61; NULL; 我们是不能修改数组名的指向的。func1() 函数是一样的&#xff0c;只是没有指定数组长度。

编译运行结果如下&#xff1a;

$ gcc 29-4.c -o 29-4
$ ./29-4
In func1: sizeof(a) &#61; 8
array[0] &#61; a
In func2: sizeof(b) &#61; 8
array[0] &#61; b

4 小结

1、数组名和指针仅使用方式相同&#xff0c;数组名本质不是指针&#xff0c;指针也不是数组名
2、数组名并不是数组的地址&#xff0c;而是数组首元素的地址
3、函数的数组参数退化为指针


推荐阅读
  • c语言基础编写,c语言 基础
    本文目录一览:1、C语言如何编写?2、如何编写 ... [详细]
  • 本文介绍了GTK+中的GObject对象系统,该系统是基于GLib和C语言完成的面向对象的框架,提供了灵活、可扩展且易于映射到其他语言的特性。其中最重要的是GType,它是GLib运行时类型认证和管理系统的基础,通过注册和管理基本数据类型、用户定义对象和界面类型来实现对象的继承。文章详细解释了GObject系统中对象的三个部分:唯一的ID标识、类结构和实例结构。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • c语言\n不换行,c语言printf不换行
    本文目录一览:1、C语言不换行输入2、c语言的 ... [详细]
  • 本文介绍了一种划分和计数油田地块的方法。根据给定的条件,通过遍历和DFS算法,将符合条件的地块标记为不符合条件的地块,并进行计数。同时,还介绍了如何判断点是否在给定范围内的方法。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • 本文介绍了C函数ispunct()的用法及示例代码。ispunct()函数用于检查传递的字符是否是标点符号,如果是标点符号则返回非零值,否则返回零。示例代码演示了如何使用ispunct()函数来判断字符是否为标点符号。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • 本文整理了315道Python基础题目及答案,帮助读者检验学习成果。文章介绍了学习Python的途径、Python与其他编程语言的对比、解释型和编译型编程语言的简述、Python解释器的种类和特点、位和字节的关系、以及至少5个PEP8规范。对于想要检验自己学习成果的读者,这些题目将是一个不错的选择。请注意,答案在视频中,本文不提供答案。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • C语言自带的快排和二分查找
    Author🚹:CofCaiEmail✉️:cai.dongjunnexuslink.cnQQ😙:1664866311personalPage&#x ... [详细]
  • 利用空间换时间减少时间复杂度以及以C语言字符串处理为例减少空间复杂度
    在处理字符串的过程当中,通常情况下都会逐个遍历整个字符串数组,在多个字符串的处理中,处理不同,时间复杂度不同,这里通过利用空间换时间等不同方法,以字符串处理为例来讨论几种情况:1: ... [详细]
  • 该楼层疑似违规已被系统折叠隐藏此楼查看此楼*madebyebhrz*#include#include#include#include#include#include#include ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有