数组名与指针的关系

  数组名与指针间的联系
  数组名与指针间的区别分析
  总结

  在刚刚接触到数组与指针时,我想最常苦恼的问题就是数组名与指针之间有什么关系了吧。但是他们两者之间真的有什么数不清道不明的关系吗?答案无疑是否定的,接下来就让我们来一探究竟吧!

  首先我们需要了解一下数组与指针的定义:

  • 数组: 有序的元素序列,数组名有限个类型相同的变量的集合命名。数组在内存中所占的大小由数组长度以及成员类型大小决定
  • 指针: 又称指针变量,在32位系统内存下占4个byte(64位系统内存下占8个byte),其中保存的值是某一块内存的地址。eg:0x0000FF00.

数组名与指针间的联系

  从定义中分析,不难发现数组名与指针并没有直接的关系,那么为什么会将数组名与指针相混淆呢?
  这是由于在近乎所有使用到数组名的表达式中,数组名都表示数组第一个元素的地址,可将其看作是一个指针常量。而这个指针常量所指向的类型与数组元素的类型一致。也正是因为如此数组名在表达式中不能作为左值,当其作为右值时其意义与&arr[0]是一致的。

  在了解到数组名相当于是一个指针常量后,那么是否可以用一个指针变量去指向一个数组呢?当然可以!如以下代码所示:

int arr[5] = {1,2,3,4.5};
int *p = arr;  //用指针变量p指向arr,p中存储的是数组首元素的地址。

数组用例
  当我们在对数组成员进行访问时就可以利用数组名下标以及指针间接访问两种方式,如以下表达式所示:

arr[ 1 ];          //访问数组中的第2个元素。
*( arr+1 );        //与 arr[ 1 ] 作用相同。
*arr + 1 ;         //*的优先级高于+,表示第一个元素的值+1。
arr[5];            //访问越界,产生随机值。
2[arr];            //与 *(arr +2)作用相同。

注意: 在以上代码中用到了指针+1的做法,这里的1个偏移量代表的数组的一个元素大小而非byte数 !!

  以上讨论的是数组名在表达式中以首元素的首地址出现的情况,但在以下两种情况中数组名代表的是整个数组。

  • sizeof(数组名) ; 计算的是整个数组在内存中的大小。
  • &数组名;取出的值与第一个元素的首地址一致,但其偏移量并非数组的一个元素大小而是整个数组的大小,如下图所示:
    这里写图片描述

数组名与指针间的区别分析

  以上讨论的是数组名与指针之间的共通之处,但并不代表说二者就是一个东西。接下来就指针与数组的定义与声明进行分析。在此之前需要再对声明与定义二者的意义进行表述。(千万不要觉得这很简单,越简单的东西越容易忽略其细节!)

  • 声明: 用于向程序表明变量的类型和名字。表示这个名字已经匹配到一块内存上了,并且声明可以出现多次。
  • 定义: 编译器创建一个对象,为这个对象分配一块内存并给它取上一个名字,并且这块内存的位置也不能被改变。

  注意!!: 二者之间最重要的区别就是定义为对象分配了内存而声明没有分配内存,只是指向了已经定义的那片内存。

  情况一:定义为指针声明为数组

//在文件text.c中对指针arr进行定义
  char* arr = "abcdefg";

----------
//在文件main.c中对arr以数组的形式进行声明并调用
#include<stdio.h>
extern arr[];
int main()
{
    printf("%s",arr);
    return 0;
}

  运行结果如下,其并没有输出我们希望的字符串“abcdefg”,那么是不是就能说明指针与数组名是不一样的呢?接下来对其进行分析。
这里写图片描述
  在文件test.c中将变量arr声明为一个指针变量,但在main.c文件中将其声明为一个数组,那么在main函数中arr就将会以数组的形式出现,并以数组的方式对arr的内容进行解析。在定义中变量arr中存储的是一个指针也就是一个地址,占用4个字节,但以char型数组进行解析时就会把地址本身的内容解析为数组元素。而arr真正指向的字符串却完全没有被访问到。原理如下图所示:
这里写图片描述
  但是有没有方法能将表达式进行改造,使arr依旧以数组的形式正确的访问到定义的字符串常量呢?接下来进行分析。
  若要访问到字符串常量就一定需要拿到字符串存储未知的地址,但是现在的地址却以内容的方式被保存在了一个字符数组里。此时arr若作为指针其偏移量是一个字节,若要一次取到四个字节的所有内容可以将arr强制转化为int*,再将其解引用就可以一次获取四个字节的内容也就是字符串地址。虽然现在拿到了地址的内容弹药利用它访问对应的地址就还需将其转化为char*类型。总结下来其表达式就是:(char*) * (int*) arr
  除此之外还有一种更为巧妙地转化方式: * (char**)arr 。在这里运用到了二级指针的相关内容,不多做解释。

  情况一:定义为数组声明为指针

//在文件text.c中对数组arr进行定义
  char arr[] = "abcdefg";

----------
//在文件main.c中对arr以指针的形式进行声明并调用
#include<stdio.h>
extern char* arr;
int main()
{
    printf("%s",arr);
    return 0;
}

代码运行崩溃….
这里写图片描述

  在文件test.c中将变量arr声明为一个字符串,但在main.c文件中将其声明为一个指针,那么在main函数中变量arr中存放的数据无论是什么类型都将以4个字节为单位解析为地址,并按照这个地址(位置地址)在内存中进行访问。从而导致程序崩溃。
  原理如下图所示:
这里写图片描述
  如果让arr继续以指针的形式出现但想正常访问到字符数组的内容有什么方法呢?接下来进行分析:
  若要访问一个数组首先要做的就是获取到这个数组的首地址,这是我们不难发现指针变量arr所占用的地址就是字符数组arr的首地址,那么只要对指针变量取地址就可以了(&arr)。此时虽然获取到了数组首元素的地址单数数组元素类型为char,内存偏移量为一个字节,而指针的内存偏移量是4个字节,如此只要将(&arr)强制转化为char*类型就可以通过加件偏移量获取到每一个数组元素。总结下来其表达式就是:(char*)& arr
  根据以上两个例子就不难发现指针与数组名虽然有联系但依旧存在有很大不同。


现将二者关系进行如下总结:

  • 数据保存方面
      指针保存的是地址(保存目标数据地址,自身地址由编译器分配),内存访问偏移量为4个字节,无论其中保存的是何种数据均已地址类型进行解析。
      数组保存的数据。数组名表示的是第一个元素的地址,内存偏移量是保存数据类型的内存偏移量;只有对数组名取地址(&数组名)时数组名才表示整个数组,内存偏移量是整个数组的大小(sizeof(数组名))。
  • 数据访问方面
      指针对数据的访问方式是间接访问,需要用到解引用符号(*数组名)
      数组对数据的访问则是直接访问,可通过下标访问或数组名+元素偏移量的方式
  • 使用环境
      指针多用于动态数据结构(如链表,等等)和动态内存开辟。
      数组多用于存储固定个数且类型统一的数据结构(如线性表等等)和隐式分配.

      本文主要用于学习总结之用,其中主要参考资料有《C语言深度解剖》,《C和指针》。内容中凡有不足疏漏之处欢迎批评指正,谢谢。

猜你喜欢

转载自blog.csdn.net/erica_ou/article/details/80809479