指针数组:是一个数组,数组元素为指针
数组指针:是一个指针,指向数组的指针
例如:
int *p1[5];
int (*p2)[5];
对于语句int*p1[5]
,因为“[]”的优先级要比*
要高,所以 p1 先与“[]”结合,构成一个数组的定义,数组名为 p1,而“int*”修饰的是数组的内容,即数组的每个元素。也就是说,该数组包含 5 个指向 int 类型数据的指针,如图 1 所示,因此,它是一个指针数组。
对于语句int(*p2)[5]
,“()”的优先级比“[]”高,*
号和 p2 构成一个指针的定义,指针变量名为 p2,而 int 修饰的是数组的内容,即数组的每个元素。也就是说,p2 是一个指针,它指向一个包含 5 个 int 类型数据的数组,如图 2 所示。很显然,它是一个数组指针,数组在这里并没有名字,是个匿名数组。
虽然有时指针数组和二维数组都可以解决同样的问题,但指针数组会更有效。
例如:
char str[n][m]={"Pascal","Basic","Fortran","Java","Visual C"};
char *ptr[n]={"Pascal","Basic","Fortran","Java","Visual C"};
(1)str[i]和ptr[i]都代表第i+1个字符串的首地址,都是对第i+1个字符串的合法引用,但str和ptr的含义完全不同。str是二维字符数组的数组名,在内存占用n*m
字节的连续储存单元,字符数组元素的初始值是初始化列表中提供的字符串的字符。支着数组ptr占内存的字节数为n*sizeof(指针类型)
。指针数组初始化列表中的字符串是存储在只读的常量存储区中的,编译时用存放这些字符串的只读存储区的首地址对指针数组进行初始化,此时指针数组的元素之(指针指向)是可以修改的,但其指向的存储单元是只读的,所以不能修改器指向的存储单元中的内容。
(2)二维数组存储多个字符串时需按最长的字符串长度来定义这个二维数组的列数,所以不管每个字符串的实际长度是否一致,它们在内存中都占用仙童长度的存储单元。而指针数组存储每个字符串的首地址时,各个字符串在内存中不占用连续的存储单元,占用存储空间由字符串的实际长度来决定,因此,使用字符串指针数组不会浪费内存空间。
(3)用二维数组存储多个字符串时,由于需要移动字符串的存储位置,所以字符串排序的速度很慢。这种通过移动字符串在实际物理存储空间中的存放位置而实现的排序,称为物理排序。而用指针数组存储每个字符串首地址时,字符串排序不需要改变字符串在内存中存放位置,只要改变指针数组中各个元素的指向即可。这样,移动指针的指向比移动字符串要快的多,这种通过移动字符串的索引地址而不是字符串的实际存储位置实现的排序,称为索引排序。显然索引排序,即指针数组处理字符串更高效。
索引拍讯程序如下:
#include<stdio.h>
#include<string.h>
#define N 5
int main()
{
int i, j;
char *temp = NULL;
char *ptr[N]= { "Pascal","Basic","Fortran","Java","Visual C" };
printf("Before sorted:\n");
for (i = 0; i < N; i++) {
puts(ptr[i]);
}
for (i = 0; i < N; i++) {
for (j = i + 1; j < N; j++) {
if (strcmp(ptr[j], ptr[i]) < 0) {
temp = ptr[i];
ptr[i] = ptr[j];
ptr[j] = temp;
}
}
}
printf("After sorted:\n");
for (i = 0; i < N; i++) {
puts(ptr[i]);
}
return 0;
}