再论数组

1.什么时候数组和指针相同

     在实际应用中,数组和指针可以互换的情形比不可互换的情形要更为常见,让我们分别考虑“声明”和“使用”这两种情况;数组声明可以分成3种情况:
     1.外部数组的声明(external array)
     2.函数定义(定义是声明的特殊情况,它分配内存空间)
     3.函数参数的声明
     所有作为函数参数的数组总是可以通过编译器转换为指针,在使用数组(在语句或表达式中的引用)时,数组总是可以写成指针的形式,两者可以互换。
 图1 指针和数组何时相同

数组和指针在编译处理时是不同的,在运行时的表示形式也是不一样的,并可能产生不同的代码。对编译器而言,一个数组就是一个地址,一个指针就是一个地址的地址

2.c标准中的数组规定

规则1:"表达式中的数组名"就是指针

        对数组下标的引用总是可以写成“一个指向数组的起始地址的指针加上偏移量”;对数组的引用如a[i]在编译时总是被编译器改成*(a+i)的形式,在表达式中,指针和数组可以互换,因为在编译器里的最终形式都是指针。

   编译器自动把下标值的步长调整到数组元素的大小。如果数组元素的大小是4字节,那么a[i]和a[i+1]在内存中的距离就是4。对起始地址执行加法之前,编译器会负责计算每次增加的步长。这就为什么指针总是有类型限制的,每个指针只能指向一个类型的原因所在:编译器需要知道对指针进行解引用操作时应该取几个字节,以及每个下标的步长应取几个字节

规则2:c语言把数组下标作为指针的偏移量

规则3:“作为函数参数的数组名”等同于指针

      在函数形参定义这个特殊情况下,编译器必须把数组形式改成指向数组第一个元素的指针形式,编译器只向函数传递数组的地址,而不是整个数组的拷贝。

例如:定义函数

void func(int *ptr){...}
void func(int ptr[]){...}
void func(int ptr[20]){...}
在func函数的调用上,实参是数组和指针都是合法的

3.数组和指针的可交换性的总结

1)用a[i]这样的形式对数组进行访问总是被编译器解释为像*(a+i)这样的指针访问。

2)指针始终是指针。它绝不可以改成数组,你可以用下标形式访问指针,一般都是指针作为函数参数时,而且你知道实际传递给函数的是一个数组。3)把一个数组定义为函数的参数时,可以选择把它定义为数组,也可以定义为指针;不管是哪种,在函数内部获得的都是一个指针。4)在其他所有情况中,定义和声明必修匹配。如果定义了一个数组,在其它文件对它进行声明时也必须把它声明为数组,指针也是如此。

4.多维数组的内存布局

在C语言中数组的元素可以是另一个数组,有助于数组的分解,例如声明如下的三维数组:

int array[2][3][5]

图 2 整个数组的内存

图 3 sizeof[i]的内存空间

图4 sizeof[i][j]的内存空间

图 5 sizeof[i][j][k]的内存空间

再如:

char ch[4][3];

有人把二维数组看作是排列在一张表格中的一行行的一维数组,如下:

                  图 6 假想的内存分布

实际的内存分布如下:

图 7 实际内存分布
      ps:对于二维数组,像图6假想的内存分布有助于平时分析,但实际的内存分布不是这样子的
     在C语言的多维数组中,最右边的下标最先变化,绝大数的语言都是采用这个约定且在堆栈内存朝低地址方向增长;
例如:
char b[2][2];


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值