A Za, A Za, Fighting...

坚信:勤能补拙

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, 
3sizeof(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, 
3sizeof(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();
}

posted on 2011-08-10 16:38 simplyzhao 阅读(105) 评论(0)  编辑 收藏 引用 所属分类: R_找工复习2011


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2011年8月>
31123456
78910111213
14151617181920
21222324252627
28293031123
45678910

统计

常用链接

留言簿(1)

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜