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语言代码:
汇编代码(gcc -S test.c)
略微对上述汇编代码进行了注释,可以发现两者的差异,指针跟数组相比,前者多了“一层间接引用”
(Note: 汇编寻址,例如,$Imm是立即数,Imm是绝对寻址(=Mem[Imm]),Ea是寄存器寻址,(Ea)则是间接寻址(=Mem[Register[Ea]]);可参考《深入理解计算机系统》第3章)
前两天看《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 ];
}
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
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语言代码:
汇编代码:
问题二:
有了前面的讲解,这里直接上代码
需要明白的就是: 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( 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
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
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 ** s = strings;
while ( * s != 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();
}
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 ** s = strings;
while ( * s != 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();
}