8.6 存放指针类型数据的数组

顾名思义,存放指针类型数据的数组就是数组中各数据元素都是由相同类型的指针组成,我们称之为指针数组,其语法为

组成部分1 组成部分2 组成部分3

类型名* 数组名称 [元素个数];

指针数组主要用于管理同种类型的指针,一般用于处理若干个字符串(如二维字符数组)的操作。使用指针数组处理多字符串数据更加方便、简洁、高效。

掌握了如何识别数组后,识别指针数组就会相对简单。既然都是数组,必然遵循数组所拥有的相关特性。但是指针数组中的数据为地址类型,需要再次进行间接访问获取数据。下面通过代码清单8-13来分析指针数组与普通类型数组的区别。

代码清单8-13 指针数组的识别—Debug版


//C++源码说明:定义指针数组、初始化

void main(){

char*pBuff[3]={//字符串指针数组定义

"Hello",//初始化字符串指针数组第1项

"World",//初始化字符串指针数组第2项

"!\r\n"//初始化字符串指针数组第3项

};

for(int i=0;i<3;i++){

printf(pBuff[i]);//显示输出字符串数组中各项

}

}

//C++源码与对应汇编代码讲解

void main(){

char*pBuff[3]={

"Hello",

;字符串数组初始化,只向数组中第1个成员赋值字符串首地址

004010F8 mov dword ptr[ebp-0Ch],offset string"Hello"(00420f84)

"World",

004010FF mov dword ptr[ebp-8],offset string"World"(00420f94)

"!\r\n"};

00401106 mov dword ptr[ebp-4],offset string"!\r\n"(0042002c)

for(int i=0;i<3;i++){

;for循环讲解略

printf(pBuff[i]);

00401125 mov ecx, dword ptr[ebp-10h];取下标值

00401128 mov edx, dword ptr[ebp+ecx*4-0Ch];一维数组寻址

0040112C push edx;将字符串首地址压入栈

0040112D call printf(00401160)

00401132 add esp,4

}

}


代码清单8-13中定义了字符串数组,该数组由3个指针变量组成,故长度为12字节。该数组所指向的字符串长度和数组本身没有关系,而二维字符数组则与之不同。代码清单8-13中的指针数组用二维数组表示如下:

char cArray[3][10]={{"Hello"},{"World"},{"!\r\n"}};

同样存储着3个字符串,但指针数组中存储的是各字符串的首地址,而二维字符数组中存储着每个字符串中的字符数据。这是它们之间本质的不同。要对它们进行区分也非常简单,分析它们的初始化过程即可。二维字符数组的初始化如下:


char cArray[3][10]={

"Hello",

mov eax,[string"Hello"(00420f84)];一维数组初始化过程

mov dword ptr[ebp-30h],eax

mov cx, word ptr[string"Hello"+4(00420f88)]

mov word ptr[ebp-2Ch],cx

mov dl, byte ptr[string"Hello"+6(00420f8a)]

mov byte ptr[ebp-2Ah],dl

xor eax, eax

mov word ptr[ebp-29h],ax

mov byte ptr[ebp-27h],al

{"World"},{"!\r\n"}};//初始化分析略


在二维字符数组初始化过程中,赋值的不是字符串地址,而是其中的字符数据,据此可以明显地区分它与字符指针数组。如果代码中没有初始化操作,那么就需要分析它们如何寻址数据。获取二维字符数组中的数据的过程如下:


printf(cArray[i]);

mov edx, dword ptr[ebp-24h]

;在二维字符数组cArray中,一维数组大小为10

;用下标值乘以0Ah以偏移到下一个一维数组首地址

imul edx, edx,0Ah

lea eax,[ebp+edx-20h];取一维数组地址

push eax

call printf(00401160)

add esp,4


虽然二维字符数组和指针数组的寻址过程非常相似,但依然有一些不同。字符指针数组寻址后,得到的是数组成员内容,而二维字符数组寻址后得到的却是数组中某个一维数组的首地址。