一个C语言指针数组和二维数组的小实验

一个C语言指针数组和二维数组的小实验

最近在用C语言写toy compiler,写的过程中突然发现自己对指针数组和二维数组的语法有了新的认识。
1. 指针数组
指针数组其实就是一个数组,数组中每个元素都是指针。
2. 二维数组
二维数组也是一个数组,不过数组中的每一个元素都是数组。

乍看起来好像很像对不对,区别只是数组元素不同,但其实这两种东西在内存中的布局完全不一样。再看下面的代码就会发现它们俩更像了:

char *a[5];         // 指针数组
char b[5][5];       // 二维数组

char c = a[3][3];
char d = b[3][3];

这段程序编译是可以通过的,这会给我们一种假象——二维数组和指针数组是一回事。
这当然是不对的,原因其实很简单,a[3][3]b[3][3]完全不是一回事,虽然长得很像。
其实如果你学过汇编或者计算机组成原理之类的课就会知道,a[3][3]其实可以分成两步:

char *tmp = a[3];
char c = tmp[3];
或者
char c = *(tmp+3);

但是再一想,b[3][3]也可以分成这两步啊,大写懵逼对不对:

char *tmp = b[3];
char d = tmp[3];
或者
char d = *(tmp+3);

区别就在于char *tmp = a[3]这一句,指针数组中a[3]的值就是a向后移动3个sizeof(char *)那个存储单元中的值。而b[3]的值是是b向后移动3*5+3sizeof(char)那个位置的地址。

很清楚了,第一个tmp的值是内存里的数据(a是一个指针数组,所以tmp的值就是指针),第二个tmp的值是地址(这个地址其实是编译器在代码生成时就静态生成了的)。

以一个例子结尾,这个例子只能在i386平台或者其他32位小端模式的CPU上运行。

#include <stdio.h>

void m(char *ar[]) {
    char a = ar[0][3];
    printf("%c", a);
}

int main() {
    char a[5][5];
    char b = 'a';
    char *pb = &b;
    pb = pb-3;
    a[0][0] = (char)((int)pb);
    a[0][1] = (char)((int)pb>>8);
    a[0][2] = (char)((int)pb>>16);
    a[0][3] = (char)((int)pb>>24);
    m((char **)a);

}

其中a是一个二维数组,而m()中的ar是一个元素是char *的数组。我的目的就是想让把a传入m()中,在m()中使用ar访问。
ar[0][3]的访问就像刚刚说的,访问的是ar[0]指向的那个数组中第三个元素。

这里写图片描述

现在我们让a的前四个元素a[0][0-3]分别赋值为&b-3的地址各八位。然后将a赋值给ar。所以ar[0][3]访问的就是由a[0][0-3]所构成的地址指向的数组的第三个成员b。这里ar[0]其实并没有指向一个数组指针,只是我们把ar[0]变成了&b-3罢了。

需要注意的是,在Intel的32位机器上地址才是4byte,4个连续的char才能存的下;如果是64位机器则有可能需要8个char才能放下,但不排除64位机器仍然使用32位地址的可能。

为此我们需要在编译时加上-m32参数
gcc test.c -m32

另外,在Intel机器上,默认采用的是小端模式存储超过一个字节的数据类型。所以我们将a[0][0]赋值为&b-3最低8位,a[0][3]赋值为最高8位。

总结

C语言的指针数组和二维数组完全不一样,虽然都是通过[][]的形式访问,但一个是运行时生成,一个是编译时生成。C语言的指针和强制类型转换可以写出强大的代码,能够修改程序内存中的任意位置,但可读性以及可移植性都大大降低,这恐怕也是Java这类语言的出现原因吧。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值