2011知识点-数组与指针

2011知识点-数组与指针

数组与指针,究竟有什么关联?什么情况下等同,什么时候不等同? 老问题,新收获
前两天看《C专家编程》,结果硬是卡在数组与指针这块好久,原本以为自己已经掌握的东西,原来还是没有掌握精髓

抛出两个问题,然后我们在解决这两个问题的过程中review指针与数组:
问题一:
char arr[] = "abcdefg";
char *ptr = "abcdefg";
我们知道,arr[i]与ptr[i]都是正确的,但两者又有什么根本的差异呢?如果是char (*pa)[10]与char **pp,那么pa[1][2]与pp[1][2]有什么区别呢?

问题二:
qsort在针对指针数组与二维数组排序时的区别?

----------------------------------------------------------------------------------------------------------

问题一:
首先,要明确一点,编译器为每个标识符分配一个地址,这个地址在编译阶段可知,相反,存储在标识符中的值只有在运行时才可知

对于数组,有 &arr = arr = &(arr[0])
对于指针,有 &ptr != ptr = &(ptr[0])
对于编译器,它所知道的是&arr与&ptr,这两个标识符的地址
基于上述三条(前两条,写个简单的程序即可测试),即可得出结论: 虽然arr[i]与ptr[i]都能正确访问某个字符(事实上,都会被编译器改写成*(arr+i)或者*(ptr+i)的形式),但是两者生成的汇编代码是不同的

验证:
C语言代码:
char  arr[]  =   " abcdefg " ;
char   * ptr  =   " abcdefg " ;

void
difference()
{
    
char  a  =  arr[ 3 ];
    
char  b  =  ptr[ 3 ];
}

汇编代码(gcc -S test.c)
difference:
    pushl    
% ebp
    movl    
% esp,  % ebp
    subl    $
16 % esp

    /* char a = arr[3] */
    movzbl    arr
+ 3 % eax /* 取地址(arr+3)处的值,放入寄存器 */
    movb    
% al,  - 1 ( % ebp)

    /* char b = ptr[3] */
    movl    ptr, 
% eax /* 取ptr的值(Mem[ptr]),放入寄存器 */
    addl    $
3 % eax
    movzbl    (
% eax),  % eax /* 将寄存器%eax的值作为地址,取该地址的值放入寄存器 */
    movb    
% al,  - 2 ( % ebp)

    leave
    ret

略微对上述汇编代码进行了注释,可以发现两者的差异,指针跟数组相比,前者多了“一层间接引用”
(Note: 汇编寻址,例如,$Imm是立即数,Imm是绝对寻址(=Mem[Imm]),Ea是寄存器寻址,(Ea)则是间接寻址(=Mem[Register[Ea]]);可参考《深入理解计算机系统》第3章)

如果是char (*pa)[10]与char **pp,pa是指向数组的指针,而pp是指向指针的指针,这里有点类似于之前的讨论
需要明白的就是: pa是指向数组的指针,保存的就是数组的地址;数组的地址等于数组首元素的地址;pa[i]等同于一元数组名称,就是指向数组首元素的指针,也保存的是数组首元素的地址,因此有: pa+i(指向数组的指针) = pa[i] = &(pa[i][0])
表达式pa[1][2]与pp[1][2]所产生的汇编代码也是不同的
(Note: 指向数组的指针p与数组名a(指向数组首元素的指针),两者的值相同,但含义不一样,例如p+1与a+1就完全不同)
验证:
C语言代码:
void
array_print(
char  ( * pa)[ 10 ])
{
    
char  c  =  pa[ 1 ][ 2 ];
}

void
pointer_print(
char   ** pp)
{
    
char  c  =  pp[ 1 ][ 2 ];
}

汇编代码:
array_print:
    pushl    
% ebp
    movl    
% esp,  % ebp
    subl    $
16 % esp

    movl    
8 ( % ebp),  % eax /* 将pa的值(Mem[pa])放入寄存器 */
    addl    $
10 % eax 
    movzbl    
2 ( % eax),  % eax
    movb    
% al,  - 1 ( % ebp)

    leave
    ret

pointer_print:
    pushl    
% ebp
    movl    
% esp,  % ebp
    subl    $
16 % esp

    movl    
8 ( % ebp),  %eax /* 将pp的值放入寄存器 */     addl    $ 4 % eax
    movl    (
% eax),  % eax
    addl    $
2 % eax
    movzbl    (
% eax),  % eax
    movb    
% al,  - 1 ( % ebp)

    leave
    ret

问题二:
有了前面的讲解,这里直接上代码
#include  " basic.h "

char   * strings[]  =  {  " zhao "
    
" pan " ,
    
" anan " ,
    NULL };

char  arr[][ 5 =  {  " zhao " ,
    
" pan " ,
    
" anan "  };

void
test_a2_addr()
{
    printf(
" Addr Testing\n " );
    printf(
" addr(arr+1): %p\n " , arr + 1 );
    printf(
" addr(arr[1]): %p\n " , arr[ 1 ]);
    printf(
" addr(arr[1][0]): %p\n " & arr[ 1 ][ 0 ]);
}

int
compare_pp(
const   void   * arg1,  const   void   * arg2)
{
    
return  strcmp( * ( char   ** )arg1,  * ( char   ** )arg2);
}

int
compare_a2(
const   void   * arg1,  const   void   * arg2)
{
    
return  strcmp( * ( char  ( * )[ 5 ])arg1,  * ( char  ( * )[ 5 ])arg2);
    
// return strcmp((char *)arg1, (char *)arg2);  // ok,too
}

void
test_pp()
{    
    qsort(strings, 
3 sizeof ( char   * ), compare_pp);

    printf(
" \nResult of qsort for POINTER-ARRAY\n " );
    
char   ** =  strings;
    
while ( * !=  NULL) {
        printf(
" %s\n " * s);
        
++ s;
    }
}

void
test_a2()
{
    qsort(arr, 
3 sizeof ( char ) * 5 , compare_a2);
    
    printf(
" \nResult of qsort for TWO-ARRAY\n " );
    
int  i;
    
for (i = 0 ; i < 3 ++ i)
        printf(
" %s\n " , arr[i]);
}

int
main(
int  argc,  char   ** argv)
{
    test_a2_addr();
    test_pp();
    test_a2();
}

你可能感兴趣的:(2011知识点-数组与指针)