数组在很多情况下是和指针等价的,数组的下标运算和指针的解引用也有等价形式:arr[i] == *(arr + 1);但是也有一些情况下数组和指针是不一样的:extern int arr[]; extern int *arr;

这里持续记录一下指针和数组的异同(以下来自于C专家编程)

首先明确一下几个定义:

  • 声明和定义:C语言中的对象必须有且只有一个定义,但是可以有多个extern声明。
定义 只能出现在一个地方 确定对象的类型并分配内存,用于创建新的对象。例如:int my_array[100]
声明 可以多次出现 描述对象的类型,用于指代其他地方定义的对象(例如在其他文件里)例如:extern int my_array[]
    • 区分定义和声明:

      • 声明相当于普通的声明:它所说明的并非自身,而是描述其他地方的创建的对象
      • 定义相当于特殊的声明:它为对象分配内存
  • 左值和右值(地址和地址的内容):

注:C语言引入了"可修改的左值"这个术语,可修改的左值指的是可以放在赋值语句左边。这个术语是为了与数组名区分,数组名也用于确定对象在内存中的位置,也是左值,但它不能作为赋值的对象。因此,数组名是个左值但不是可修改的左值。

  1. 指针与数组的不同:
    • 编译器为每个变量分配一个地址(左值),这个地址在编译时可知,而且该变量在运行时一直保存于这个地址。相反,存储于变量中的值(它的右值)只有在运行时才可知。如果需要用到变量中存储的值,编译器就要发出指令从指定地址读入变量值并将它存于寄存器中。所以,如果编译器需要一个地址来执行某种操作,它就可以直接进行操作(因为符号的地址在编译时可知),并不需要增加指令首先取得具体的地址。相反,对于指针,必须首先在运行时取得它的当前值(取得指针本身所存的右值),然后才能对它进行解除引用操作。
    • 故:int a[100] 和 extern int a[]都表明a是一个数组,也就是一个内存地址,数组内的字符可以从这个地址找到。从数组提取一个字符,只要简单的从符号表显示的a的地址加上下标,需要的字符就位于这个地址中。相反,如果声明extern int *p,它告诉编译器p是一个指针,它指向的对象时一个字符。为了取得这个字符,必须得到地址p的内容(注意数组a本身就是一个地址),把它作为字符的地址并从这个地址中取得这个字符,相比数组a来说多了一次额外的提取。取a中的字符和p所指的字符见下图:

    • 由上可知,int a[100] 告诉编译器a是一个数组,本身就是一个地址。int *p告诉编译器p是一个指针,p所在的地址中存的是一个int的地址。

下面继续对数组和指针进行解释:"当定义为指针,但以数组方式引用"时会发生什么

    • 这时编译器所执行的是对内存进行间接引用。之所以会如此是因为我们告诉编译器我们拥有的是一个指针。

对照上图的访问方式:
                    char *p = "abcdefg";    ...... p[3]
                    和
                    char a[] = "abcdefg";    ...... a[3]
                    这两种情况都可以取得字符'd',但两者的途径却很不一样。

当写了extern char *p的时候,编译器将会:
                     a.取得符号表中p的地址,提取存储于此处的指针
                     b.把下标所表示的偏移量与指针的值相加,产生一个地址
                     c.访问上面这个地址,取得字符。

    • 考虑如果p被声明为extern char *p;但是它原先的定义却是char p[10];这种情形。当用p[i]这种形式提取这个声明的内容的时候,实际上得到的是一个字符。但是按照上面的方法,编译器确把它当成是一个指针,就会先去取这个地址中所存的地址,但是其实p的地址中现在存的其实已经是一个int型了,吧ASCII字符解释为地址显然是驴唇不对马嘴。相反的,如果p被声明为extern char p[];但是它原先定义却是int *p;这种情形的话,那么p[i],编译器会直接取地址(p + i),显然这也是不对的,因为此时直接把p的地址和i相加其实是一个不存在的地址。
    • 可见数组和指针在这种情况下是不同的。在C语言中,对数组的引用其实引用的是地址本身(不可变左值),但是对指针的引用其实是引用指针所在地址中的内容(右值)。

数组和指针的其他区别

指针 数组
保存数据的地址 保存数据

间接访问数据,首先取得指针的内容,把它作为地址,然后从这个地址提取数据。

如果指针有一个下标[I],就把指针的内容加上I作为地址,从中提取数组

直接访问数据,a[I]只是简单的一a + I为地址取得数据
通常用于动态数据结构 通常用于存储固定数目且数据类型相同的元素
相关的函数为malloc(),free() 隐式分配和删除
通常指向匿名数据 自身即为数据名

2.数组和指针的相同点:


除了上面的几种不同的情况以外,很多情况下数组和指针是可以等同看待的,例如在作为函数的形参的时候,数组和指针是等价的:void foo(int *); == void foo(int[]);

很多情况下我们使用数组和指针的时候也是非常类似的,例如 arr[n] == *(arr + n)

C中 数组和指针的异同的更多相关文章

  1. C语言中数组与指针的异同之处!你不知道的编程奥秘~

    C语言的数组和指针一直是两个容易混淆的东西,当初在学习的时候,也许为了通过考试会对指针和数组的一些考点进行突击,但是很多极其细节的东西也许并不是那么清楚.本篇侧重点在于分析数组与指针的关系,什么时候数 ...

  2. C/C++中数组与指针的关系探究

    数组与指针 长期以来,在C/C++中,数组名和指向数组首元素的指针常量到底是以一种什么关系,一直困扰着很多人.很多地方,甚至是一些教科书中都在说,"数组名就是一个指向数组首元素的指针常量&q ...

  3. 《挑战30天C++入门极限》新手入门:C/C++中数组和指针类型的关系

        新手入门:C/C++中数组和指针类型的关系 对于数组和多维数组的内容这里就不再讨论了,前面的教程有过说明,这里主要讲述的数组和指针类型的关系,通过对他们之间关系的了解可以更加深入的掌握数组和指 ...

  4. 由strcat函数引发的C语言中数组和指针问题的思考

    问题一 首先,来看一下下面这段代码: #include <stdio.h> #include <string.h> int main() { char *str = " ...

  5. c语言中数组,指针数组,数组指针,二维数组指针

    1.数组和指针 ] = {,,,,};// 定义数组 // 1. 指针和数组的关系 int * pa = array; pa = array; // p[0] == *(p+0) == array[0 ...

  6. C中数组与指针【转】

    在这里随便定义一个数组 int arr[5]; arr现在就是数组名, arr 代表的是该数组整块内存,即sizeof(arr) == 20 (假设sizeof(int) == 4), arr 里的内 ...

  7. C 中数组和指针的区别

    联系: 1,一个通过数组和下标实现的表达式可等价地通过指针和偏移量实现. 2,当数组名传递给一个函数时,实际上传递的是该数组第一个元素的地址. 区别: 1,指针是一个变量,因此,在C语言中,语句pa= ...

  8. php中数组的指针

    利用PHP内置的函数 key() 获得键. current()获得值, next(); prev();移动到上一个 reset();//重置,移动到第一个元素 end();//移动到最后一个元素上 注 ...

  9. 深入理解C/C++数组和指针

    C语言中数组和指针是一种很特别的关系,首先本质上肯定是不同的,本文从各个角度论述数组和指针. 一.数组与指针的关系数组和指针是两种不同的类型,数组具有确定数量的元素,而指针只是一个标量值.数组可以在某 ...

随机推荐

  1. P1759 通天之潜水(不详细,勿看)(动态规划递推,组合背包,洛谷)

    题目链接:点击进入 题目分析: 简单的组合背包模板题,但是递推的同时要刷新这种情况使用了哪些物品 ac代码: #include<bits/stdc++.h> using namespace ...

  2. 笔试算法题(41):线索二叉树(Threaded Binary Tree)

    议题:线索二叉树(Threaded Binary Tree) 分析: 为除第一个节点外的每个节点添加一个指向其前驱节点的指针,为除最后一个节点外的每个节点添加一个指向其后续节点的指针,通过这些额外的指 ...

  3. 笔试算法题(22):二分法求旋转数组最小值 & 骰子值概率

    出题:将一个数组最开始的k个(K小于数组大小N)元素照搬到数组末尾,我们称之为数组的旋转:现在有一个已经排序的数组的一个旋转,要求输出旋转数组中的最小元素,且时间复杂度小于O(N): 分析: 时间复杂 ...

  4. 在springBoot的控制台打印sql语句

    在springBoot+Mybatis日志显示SQL的执行情况的最简单方法就是在properties新增: properties形式 logging.level.com.eth.wallet.mapp ...

  5. Spider-Python爬虫之聚焦爬虫与通用爬虫的区别

    为什么要学习爬虫? 学习爬虫,可以私人订制一个搜索引擎. 大数据时代,要进行数据分析,首先要有数据源. 对于很多SEO从业者来说,从而可以更好地进行搜索引擎优化. 什么是网络爬虫? 模拟客户端发送网络 ...

  6. 【BZOJ 1202】 [HNOI2005]狡猾的商人(枚举区间也可行)

    题链:http://www.lydsy.com/JudgeOnline/problem.php?id=1202 其实也可以不使用加权并查集,通过画图可以发现,一个长区间和其包含的区间能够算出一个新区间 ...

  7. 一个WebLoad 脚本范例

    //initial the Agenda function InitAgenda(){     wlGlobals.SaveHeaders = true;     wlGlobals.SaveSour ...

  8. __asm

    来源:http://msdn.microsoft.com/zh-cn/library/45yd4tzz.aspx Microsoft 专用 __asm 关键字调用一个内联汇编,并且可以显示,每当 c. ...

  9. sql语句在Mysql中如何执行?

    1.MySQL 主要分为 Server 层和引擎层,Server 层主要包括连接器.查询缓存.分析器.优化器.执行器,同时还有一个日志模块(binlog),这个日志模块所有执行引擎都可以共用,redo ...

  10. Redis持久化方式--RDB和AOF

    转载于:https://www.cnblogs.com/xingzc/p/5988080.html Redis提供了RDB持久化和AOF持久化 RDB机制的优势和略施 RDB持久化是指在指定的时间间隔 ...