指针与数组关系

指针是C语言中的精髓。《高质量C++编程指南》的作者林锐就曾说过:不会正确使用指针,肯定算不上是合格的程序员。昨晚我思考了一宿,自认找到了理解指针的正确途径。本文试图通过探究指针和数组的关系去研究指针。

一般的C语言教科书上都会有这样的话:指针就是地址,数组名就是指针的首地址。这些不能不说是错误的,但是却没有深入进去,学生很难有较深的理解。

我认为从本质上看,数组是一个单独的内存块,指针是单独一个内存单元。这个原理也许谁都懂。理解指针和数组,我觉得还要理解编译器的运行原理。比如,简单的如下面的几行代码,你知道编译器做了哪些工作吗?

  1. float x=123.456;
  2. double p=3.1415926;
  3. printf("%7.2f /n",x);
  4. printf("%7.2lf /n",p);
  5. printf("%-7.2f /n",x);
  6. printf("%-7.2lf /n",p);

上面几行代码,在VC中编译往往会出现一个警告:warning C4305: ‘initializing’ : truncation from ‘const double’ to ‘float’。大意是将double型数据非法截断为float型。这个警告是针对第一行的。也许你会说:123.456不是浮点型吗?原因在哪儿呢?
这简单的一行程序,对于编辑器来说,要做两个步骤:一是开劈变量,二是斌值。然而,编译器在这两步前就已经做了一步:开劈常量。注意:你会说这个程序里压根儿没有const。
但是,程序在这样处理的:在开劈变量之前,首先把你的程序扫描一遍,凡是你输入的数值、字符全部视为常量存放起来。所以,这么简单的一句话,不仅仅是开劈一个变量,
在此之前早就开劈了一个常量。它的类型是double,值为2.34。你所要做的,就是提醒译器“这是float型”,你把这行改为:float x = 2.34f;即可。现在你还看不到这个程序和我们要谈的话题的关系,但等下你会看到的。

本文试图通过一些例子来说明问题。首先我想请读者思考下面的问题:

例子一是那个著名的字符串复制程序:

  1. #include<stdio.h>
  2. #include<iostream.h>
  3. void strcpy(char* strDin,char* strSrc)
  4. {
  5. while(*strSrc!=’/0′)
  6. {
  7. strDin++=strSrc++;
  8. }
  9. }
  10. void main()
  11. {
  12. char p[15]="hello world";
  13. char q[20];
  14. strcpy(q,p);
  15. printf("%s",q);
  16. }

这个程序会有一个错误。错误行为 strDin++=strSrc++;编译器会提示说:=左边应是一个左值。你会问:为什么会有这个错误?我暂时不公布答案,我们继续看例子。

这是一个《高质量C++编程指南》里的例子:

  1. char a[] = “hello”;
  2. a[0] = ‘X’;
  3. cout << a << endl;
  4. char *p = “world”;
  5. p[0] = ‘X’;
  6. cout << p << endl;

注意这个程序编译是并没有错误,但运行时会出现内存不能written的错误。为什么?

例子三:

  1. #include <iostream.h>
  2. int main(void)
  3. {
  4. char a[20];
  5. a="hello";
  6. cout<<a<<endl;
  7. return 1;
  8. }

这个程序会有一个错误:’=’ : cannot convert from ‘char [6]‘ to ‘char [20]‘。大意是=不能将char [6]转化为char [20]。你可能会问:为什么?

现在我开始公布我的思考成果。我觉得问题的本质是一个常量与变量的问题,或者说是一个权限问题。为什么 strDin++=strSrc++;这句不能实现字符复制。其实我们把这行代码分解一下就会明白.

strSrc++;

strDin++;

strDin= strSrc;

首先我们明确strDin和strSrc都是一个字符指针变量,它有权改变放在自己的值。比如比如你可以给strDin和strSrc赋任何值。

问题是你有权改变自己,却无权改变他人。假设strDin对应的值是1000,strDin对应的值是2000,

strSrc++; // 这时strSrc变为1002

strDin++; // 这时strDin变为2002

strDin= strSrc; //strDin==2002。

你想这样有何不可呢?这样是可以的。但是你不要忘记,strDin是一个字符指针,也就是说strDin是一个内存地址,当strDin==2002时,你想你有权力改变2002这个内存单元的值吗?显然这样是无法复制字符串的。

在例子2中你会看到char a[]变为“Xhello”.但是字符串p并没有改变。你可能会为p抱不平:为什么数组a的字符可以改变,而指针p不能改变呢?这时因为“world”是一个常量字符串。char *p = “world”; 这一句编译器首先是找一个内存块把“world”这个常量字符串存诸起来,然后把首地址赋给p。因为这是一个常量字符串,也就是说它的字符是不能改变的。你可以去读这个字符串,却不能改动这个内存块的任何内容。那为什么数组a可以改变呢?因为数组a开辟的是一个内存块,它对这个内存块既可读又可写。这时你可能会说:数组可以转化为用指针表示,而指针却不一定能转化为数组。这种说法是有一定道理的。

在例子3,为什么不能这样给字符数组a赋值呢?道理是一样的。a="hello";只是把常量字符串的首地址赋给a,但是数组a的首地址是一个常量,在定义时编译器已经开辟了一个空间给它。你可能会问:为什么char a[]="hello";这时因为数组a对开辟的这个内存块拥有绝对的权力,既可以读又可以写。这好比某个人在城东有一块土地,他可以在这块土地上种任何庄稼,但他却不能将这块土地从城东搬到城西。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值