设为首页 加入收藏

TOP

深入理解C/C++数组和指针(一)
2019-06-12 16:06:14 】 浏览:109
Tags:深入 理解 C/C 指针

转载:http://blog.csdn.net/walkinginthewind/article/details/7044380

 

C语言中数组和指针是一种很特别的关系,首先本质上肯定是不同的,本文从各个角度论述数组和指针。

 

一、数组与指针的关系

数组和指针是两种不同的类型,数组具有确定数量的元素,而指针只是一个标量值。数组可以在某些情况下转换为指针,当数组名在表达式中使用时,编译器会把数组名转换为一个指针常量,是数组中的第一个元素的地址,类型就是数组元素的地址类型,如:

int a[5]={0,1,2,3,4}; 

数组名a若出现在表达式中,如int *p=a;那么它就转换为第一个元素的地址,等价于int *p=&a[0];

再来一个:

int aa[2][5]={0,1,2,3,4,   

                      5,6,7,8,9};

数组名aa若出现在表达式中,如int (*p)[5]=aa;那么它就转换为第一个元素的地址,等价于int (*p)[5]=&aa[0]; 

但是int (*p)[5]=aa[0]; 这个就不对了,根据规则我们推一下就很明了了,aa[0]的类型是int [5],是一个元素数量为5的整型数组,就算转化,那么转化成的是数组(int [5])中第一个元素的地址&aa[0][0],类型是 int *。所以,要么是int (*p)[5]=aa;要么是int (*p)[5]=&aa[0];

只有在两种场合下,数组名并不用指针常量来表示--就是当数组名作为sizeof操作符或单目操作符&的操作数时,sizeof返回整个数组的长度,使用的是它的类型信息,而不是地址信息,不是指向数组的指针的长度。取一个数组名的地址所产生的是一个指向数组的指针,而不是指向某个指针常量值的指针。

如对数组a,&a表示的是指向数组a的指针,类型是int (*) [5],所以int *p=&a;是不对的,因为右边是一个整形数组的指针int (*)[5],而p是一个整形指针int *;

数组的sizeof问题会在下面中仔细讨论。

二、数组与指针的下标引用

int a[5]={0,1,2,3,4}; 

如a[3],用下标来访问数组a中的第三个元素,那么下标的本质是什么?本质就是这样的一个表达式:*(a+3),当然表达式中必须含有有效的数组名或指针变量。

其实a[3]和3[a]是等价的,因为他们被翻译成相同的表达式(顶多顺序不同而已),都是访问的数组a中的元素3。

指针当然也能用下标的形式了,如:int *p=a; 那么p[3]就是*(p+3),等同于3[p](不要邪恶。。。3P,3P),同样访问数组a中的元素3。

根据这一规则,我们还能写出更奇怪的表达式,如:

int aa[2][5]={0,1,2,3,4,

                      5,6,7,8,9};

1[aa][2],这个看起来很别扭,首先 1[aa],就是*(1+aa),那么1[aa][2]就是*(*(1+aa)+2),也就是aa[1][2]。

1[2][aa],这个就不对了,因为前半部分1[2]是不符合要求的。

当然在实际中使用这样的表达式是没有意义的,除非就是不想让人很容易的看懂你的代码。

三、数组与指针的定义和声明

数组和指针的定义与声明必须保持一致,不能一个地方定义的是数组,然后再另一个地方声明为指针。

首先我们解释一下数组名的下标引用和指针的下标应用,它们是不完全相同的,从访问的方式来讲。

int a[5]={0,1,2,3,4};

int *p=a;

对于a[3]和p[3]都会解析成*(a+3)和*(p+3),但是实质是不一样的。

首先对于a[3],也就是*(a+3):

(1)把数组名a代表的数组首地址和3相加,得到要访问数据的地址,这里注意,数组名a直接被编译成数组的首地址;

(2)访问这个地址,取出数据。

对于p[3],也就是*(p+3):

(1)从p代表的地址单元里取出内容,也就是数组首地址,指针名p代表的是指针变量的存储地址,变量的地址单元里存放的才是数组的首地址;

(2)把取出的数组首地址和3相加,得到要访问的数据的地址;

(3)访问这个地址,取出数据。

下面给出一个例子来说明若定义和声明不一致带来的问题:

设test1.cpp中有如下定义:

char s[]="abcdefg";

test2.cpp中有如下声明:

extern char *s;

显然编译是没有问题的。

那么在test2.cpp中引用s[i]结果怎样呢?如s[3],是‘d’吗?好像是吧

下面我们对test2.cpp中的s[3]进行分析:

s的地址当然是由test1.cpp中的定义决定了,因为在定义时才分配内存空间的;

我们根据上面给出的指针下标引用的步骤进行计算

(1)从s代表的地址单元的取出内容(4个字节),这里实际上是数组s中的前4个元素,这个值是“abcd”,也就是16进制64636261h,到这一步应该就能看出来问题了;

(2)然后把取出的首地址和3相加,得到要访问的数据的地址64636261h+3,这个地址是未分配未定义的;

(3)取地址64636261h+3的内容,这个地址单元是未定义的,访问就会出错。

下面给出分析的代码(可只需观察有注释的部分):

#include<iostream>         
using namespace std;
extern void test();
char s[]="abcdefg";
int main()
{
002E13A0  push        ebp  
002E13A1  mov         ebp,esp  
002E13A3  sub         esp,0D8h  
002E13A9  push        ebx  
002E13AA  push        esi  
002E13AB  push        edi  
002E13AC  lea         edi,[ebp+FFFFFF28h]  
002E13B2  mov         ecx,36h  
002E13B7  mov         eax,0CCCCCCCCh  
002E13BC  rep stos    dword ptr es:[edi]  
    char ch;
    int i=3;
002E13BE  mov         dword ptr [ebp-14h],3  
    ch = s[i];
002E13C5  mov         eax,dword ptr [ebp-14h]  
002E13C8  mov         cl,byte ptr [eax+011F7000h]  /* s直接翻译成数组首地址和i(eax)相加,得到操作数地址,然后作为byte ptr类型取内容,传给cl */
002E13CE  mov         byte ptr [ebp-5],cl          /* cl的内容传给ch(ebp-5) */
    test();
002E13D1  call        002E1073  
    return 0;
002E13D6  xor         eax,eax  
}
002E13D8  pop         edi  
002E13D
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇ARTS 第一周打卡 下一篇c++ 【递归算法】梵塔问题

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目