C 总结——指针和数组是不同的两种东西

抄录

请记住 Peter Norvig 说的,学会编程通常需要十年,为何人人都这么着急?

无符号、有符号运算

当有符号和无符号数在同一个运算里的话,是将有符号提升为无符号数运算。

    int x;
    unsigned int  y,z;
    x = -1;
    y = 2;
    z = x * y;

z 的二进制结果为:0xfffffffe。

    int x;
    unsigned int  y,z,k,m;
    x = -2;
    y = 1;
    z = 500 + (x + y) * 100;
    k = (x + y);
    m = k*100+500;

z = 400; k = 4294967295; m = 400;
虽然结果正确,但是并不表示过程是正确的。

数组和指针

例1:

    int a[10];
    int *aa;

    for (i = 0; i< 10; i++)
    {
        a[i] = i+1;
    }
    aa = (int **) &a;
    **aa = 1;

编译没有报错,运行时提示为无效指针写错误。
例2

    int a[10];
    int **aa;
    aa = (int **) &a;
    *aa = a;
    *(*aa + 0) = 0;

编译无错,运行无错,a[0] = 0;
例3

    int a[10];
    int **aa;
    aa = (int **) &a;
    *aa = a;
    *(*aa + 0) = 0;
    *(*aa + 1) = 1;

编译没有报错,运行时提示为无效指针写错误。
例4

int i ;
    int a[10];
    for (i = 0; i< 10; i++)
    {
        a[i] = i;
    }
    int **aa;
    aa = (int **) &a;
    *aa = a;

编译、运行没有错误,但是a[0]中的数值改变且变为自身a的地址。

观察调试环境

观察调试环境
可以发现一维数组a的地址跟其指向的地址相同。如果硬要把a理解为指针,那么a就是指向自己本身的指针。
然而定义一个指针变量int *p,包含了两层意思,首先p是一个变量,变量就有自己存储空间,其地址为&p。其次p存储空间中可以存储数据。以下图为例. *p = 1 是将数值1存放到地址为0xffff1111的内存中。
a02
显然把一维数组a理解为指向自身的指针是有问题的,将下图理解为一维数组的话,a[0] = 1的结果是地址0xffff0000中值变为1,一维指针里存放的数据应该是一个地址,而非要赋值的数据。
a03
综上,将一维数组中int a[10]的a理解为地址数值更合适。所以&a理解对数值取地址还是数值本身。

分析

例1的图示如下。aa取得的数据为0xffff1111, *aa取得的数据为1,**aa=1就是将数值1赋值给地址1,显然0x0000001为无效地址。
a04

同理可以分析例2和例3 , 其中*(*aa+1)= 1是将数值1赋值给地址为0x00000005的内存,显然也是无效内存。
a05

综上,可以说明一维数组变量仅仅只能代表一个地址数值,跟一维指针还是有所不同的。那么二维数组呢?
还是先看看一维数组的汇编代码。

    int *p;
    int a[10];

    a[0]= 1;
    a[1]= 2;
    a[2]= 3;

    p = a;
    *p = 1;
    *(p+1)= 2;
//汇编代码
    int *p;
    int a[10];
    a[0]= 1;
00EF352E  mov         dword ptr [a],1 
    a[1]= 2;
00EF3535  mov         dword ptr [ebp-34h],2 
    a[2]= 3;
00EF353C  mov         dword ptr [ebp-30h],3 

    p = a;
00EF3543  lea         eax,[a] 
00EF3546  mov         dword ptr [p],eax 
    *p = 1;
00EF3549  mov         eax,dword ptr [p] 
00EF354C  mov         dword ptr [eax],1 
    *(p+1)= 2;
00EF3552  mov         eax,dword ptr [p] 
00EF3555  mov         dword ptr [eax+4],2 

观察a[1]= 2;*(p+1)= 2;处的汇编代码,可以看出对数组的取地址行为是编译器计算出相对于函数栈的偏移地址,而对于指针则是编译器计算出相对于指针地址的偏移地址。进一步说明二者的不同。

观察二维数组可以得出同样结论。

    int b[2][2];

    b[0][0] = 1;
0116352E  mov         dword ptr [b],1 
    b[0][1] = 2;
01163535  mov         dword ptr [ebp-10h],2 
    b[1][1] = 4;
0116353C  mov         dword ptr [ebp-8],4 

如下贴出二维数组和二维指针的一个错误例子,错误的关键就是数组的取地址方式跟指针的取地址方式不一样。

    int **p;
    int b[2][2];

    b[0][0] = 1;
003A352E  mov         dword ptr [b],1 
    b[0][1] = 2;
003A3535  mov         dword ptr [ebp-1Ch],2 
    b[1][1] = 4;
003A353C  mov         dword ptr [ebp-14h],4 

    p = (int **)b;
003A3543  lea         eax,[b] 
003A3546  mov         dword ptr [p],eax 

    p[1][1] = 1;
003A3549  mov         eax,dword ptr [p] 
003A354C  mov         ecx,dword ptr [eax+4] 
003A354F  mov         dword ptr [ecx+4],1 

然而一维数组变量是可以直接赋值给一维指针的,但是这只是运算结果相等的巧合,所以还是那句话,结果正确并不代表过程也正确。

最后总结一点,数组更好的迎合了栈的使用,而指针更好的迎合了堆的使用,C语言之所以经得起时间的考验就不太可能存在“既生瑜何生亮”的两种东西。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值