《C专家编程》:令人震惊的事实:数组和指针并不相同

数组和指针是如何访问的

首先需要注意的是“地址y”和“地址y的内容”之间的区别。

X = Y;

在这个上下文环境里

符号X的含义是X所代表的地址,这里被称为左值,左值在编译时可知,左值表示存储结果的地方

符号Y的含义是Y所代表的地址的内容,这里被称为右值,右值直到运行时才知,如无特别说明,右值表示“Y的内容”

C语言引入了“可修改的左值”这个术语。它表示左值允许出现在赋值语句的左边。这个奇怪的术语是为与数组名区分,数组名也用于确定对象在内存中的位置,也是左值,但它不能作为赋值的对象。因此,数组名是个左值但不是可修改的左值。标准规定赋值符必须用可修改的左值作为左边一侧的操作数。用通俗的话说,只能给可以修改的东西赋值。

编译器为每个变量分配一个地址(左值)。这个地址在编译时可知,而且该变量在运行时一直保存于这个地址。相反,存储于变量中的值(它的右值)只有在运行时才可知。如果需要用到变量中存储的值,编译器就发出指令从指定地址读入变量值并将它存入寄存器中。

这里的关键之处在于每个符号的地址在编译时可知。所以,如果编译器需要一个地址(可能还需要加上偏移量)来执行某种操作,它就可以直接进行操作,并不需要增加指令首先取得具体的地址。相反,对于指针,必须首先在运行时取得它的当前值,然后才能对它进行解除引用操作(作为以后进行查找的步骤之一)。

数组的下标引用:

char a[9] = "abcdefgh";
c = a[i];

编译器符号表具有一个地址9980

运行时步骤1:取i的值,将它与9980相加

运行时步骤2:取地址(9980 + i)的内容

9980    +1     +2        +3     +4..........+i

对指针的引用:

char *p;
c = *p;

编译器符号表有一个符号p,它的地址为4624

运行时步骤1:取地址4624的内容,就是‘5081’

运行时步骤2:取地址5081的内容

当你“定义为指针,但以数组方式引用”时会发生什么

char *p = "abcdefgh";
c = p[i];

编译器符号表具有一个p,地址为4624

运行时步骤1:取地址4624的内容,即 5081

运行时步骤2:取得i的值,并将它与5081相加

运行时步骤3:取地址[ 5081+i ]的内容

最后一种方式其实质是前两种访问方式的组合。先间接引用,在直接引用

  • 取得符号表中p的地址,提取存储于此处的指针
  • 把下标所表示的偏移量与指针的值相加,产生一个地址
  • 访问上面这个地址

只要把p声明为指针,那么不管p原先是定义为指针还是数组,都会按照上面所示的三个步骤进行操作,但只有当p原来定义为指针时这个方法才是正确的。

 文件1:

char p[10];

文件2:

extern char*p;

这种情况,当用p[i]这种形式提取这个声明的内容时,实际上得到的是一个字符,但按照上面的方法,编译器却把它当做指针,把ACSII解释为地址显示不对。

char *p = "abcdefgh"
p[3];
char *a = "abcdefgh"
a[3];

显然这两种情况下,都可以取得‘d’,但两者的途径不一样

数组与指针的其他区别


表三:数组与指针的区别

数组和指针都可以在它们的定义中用字符串常量进行初始化,尽管看上去一样,底层的机制却不相同

定义指针时,编译器并不为指针所指向的对象分配空间,它只是分配指针本身的空间,除非在定义时同时赋给指针一个字符串常量进行初始化,在ACSI C中,初始化指针所创建的字符串常量被定义为只读,如果试图通过指针修改这个字符串的值,程序就会出现未定义的行为,是因为:指针p 指向常量字符串(位于常量存储区),常量字符串的内容是不可以被修改的。

与指针相反,由字符串常量初始化的数组是可以修改的

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值