C语言——指针与数组区别

数组与指针的讨论

数组定义并不等同于指针的外部声明!

声明?定义?

定义  只能出现在一个地方   确定对象的类型并分配内存,用于创建心得对象,如:int my_array[100];
声明  可以多次出现        描述对象的类型,用于指代其他地方定义的对象(例如在其他文件里),如:extern int my_array[]
extern对象声明告诉编译器对象的类型和名字,对象的内存分配则在别处进行,由于并未在声明中分配内存,
所以并不需要提供数组长度信息,对于多维数组,需提供处最左边一维外其他维长度。

定义是一种特殊的声明,它创建了一个对象;声明简单地说明了在其他地方创建的对象的名字,它允许你使用这个名字。

数组和指针的访问

出现在赋值符号左边的符号有时被称为左值,右边的有时被称为右值。
编译器为每个变量分配一个地址(左值),这个地址在编译时可知,而且该变量在运行时移植保存这个地址,
相反,存储于变量中的值(它的右值)只有在运行时可知,若需用到变量中存储的值,编译器就发出指令从制定地址读入变量值存于寄存器
A.数组的下标引用:
            char a[9] = "abcdefgh";
            ...
            c = a[i];

            ————  ————  ————  ————  ————  ————  ————  ————
            9980   +1    +2    ..........  +i 

            编译器符号表具有一个地址9980: 运行时步骤1:取i的值,将它与9980相加
                                       运行时步骤2:取地址(9980+i)地址内容
B.对指针的引用:
            char *p;
            ...
            c = *p;
                               ————
            —— —— —— ——       |    |
           |5 |0 |8 |1 | ----> ————
            —— —— —— ——        5081   
        -->4624
            编译器符号表有一个符号p,它地址位4624:运行时步骤1:取地址4624内容,就是‘5081’
                                              运行时步骤2:取地址508内容

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



每个符号的地址在编译时可知。所以如果编译器需要一个地址(可能还要加上偏移量)来执行某种操作,他就可以直接进行操作
并不需要增加指令首先取得地址。相反对于指针,必须先在运行时取得它的当前值,然后才能对它进行解除引用操作(指针的概念
和原理非常多,但实质其实就两个字:引用!这个引用所引用的是某个实体,所谓解引用其实就是“取”出指针引用的实体的意思)

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

C.对指针进行下标引用:
                  char *p = "abcdfgh";
                  ...
                  c = p[i];

                 —— —— —— ——                      /---------------------------|
                |5 |0 |8 |1 | ----> (5081+i)-----/  ———— ———— ———— ———— ———— ———— ———— ————
                 —— —— —— ——                        5081  +1   +2   +3  ....  +i
             -->4624
                  编译器符号表具有一个p,地址位4624:
                                                运行时步骤1:取地址4624内容,即‘5081’
                                                运行时步骤2:取得i的值,并与5081相加
                                                运行时步骤3:取地址[5081+i]的内容
对照图C的访问方式:
char *p = "abcdefgh"; ..... p[3]
和图A的访问方式:
cahr a[] = "abcdefgh"; .... a[3]
这两种情况下都可以取得字符‘d’但两者途径非常不同。
1.取得符号表中p的地址,提取存储于此处的指针。
2.把下标所表示的偏移量与指针相加,产生一个地址。
3.访问上面的地址,取得字符。

编译器已被告知p是个指向字符的指针(数组定义告诉编译器p是个字符序列)。p[i]表示“从p开始前进i步,每步都是个字符(即每个元素长度1字节)”
如果是其他类型指针,其步长也各不相同。



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

例如在1-1.c中这样定义:cahr p[] = "abcd";
在1.c中这样声明:extern cahr *p;

按照上面的方法,编译器会把它当成一个指针,把ACSII字符解释为地址,很显然会内存错误(不信可以试试,代码目录里面有);

在ANSI C中,初始化指针时所创建的字符串常量被定义为只读,如果试图通过指针修改这个字符串的值,程序会出现未定义行为。
相反,数组可以

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值