数组与指针详解

来源:互联网 发布:宏观经济基础数据库 编辑:程序博客网 时间:2024/05/19 02:30

一、数组与指针的基本关系

C语言的初学者,对数组和指针的关系含混不清,其实一句话就可以道出两者的关系——“在C语言中, 只有指针算术和数组下标运算是等价的,指针和数组是不同的”。

         很多人认为char a[]与char *a是等价的,也许他们看到的是在函数的形参中char a[]与char *a可以相互替换,而误以为两者等价,实则完全不然。

         例如:若char a[]与char *a等价,下面的例子执行的时候就不会出错。

         文件1:test_array_point.c

 

          1 #include <stdio.h>

     2  char a[30]="test array and point";

     3  extern void test_func();

     4  int main(int argc, char **argv){

     5          test_func();

     6          printf("test!\n");

     7          return 0;

            8  }

         文件2:test_extern_array_by_point.c

     1 #include <stdio.h>

     2

     3  extern char *a;

     4

     5  void test_func(){

     6          printf("test_func a=%s\n",a);

             7}

         执行结果:Segmentationfault (core dumped)

出现了段错误,但是“extern char*a;”改为“extern char a[];”,程序就能正确执行,由此简单实例看来数组和指针不是等价的。

         再看一下数组和指针在声明定义时的区别

         char a[6]=”hello”;和char *p=”world”;

         数组定义char a[6]请求系统在内存中创建6个字节的空间,并用名字a表示。而指针声明char*p请求一个位置放置一个指针,并用名称p表示,这里的数组和指针初始化后的逻辑如下图:


         接下来再看一下数组和指针在访问时的区别:

以第四个位置元素为例,也即a[3]和p[3],当编译器看到表达式a[3]的时候,它生成的代码从a的位置开始向后跳过3个,然后取出那个字符;若编译器看到p[3]时,它生成的代码找到p的位置,取出其中的指针值,在指针值上加3,然后取出指向的字符。数组和指针的本质区别在于类似a的数组和类似p的指针一旦在表达式中出现就会按照不同的方法计算,不论它们是否有下标。

 

还是回到一开始的那个问题:char a[]与char *a是等价的,为什么很多人都认为它们是等价的呢?一定有相似点的存在才会有这样的理解。

         是的,在函数的形参中他们是相同的,例如:

void f(char a[]){

         ……

}

void f(char *a){

         ……

}

是一样的。这是编译器做的小动作的原因,数组在传入函数时,函数接收到的不是数组本身,而是转化后的指针,指针类型与数组名的类型相同。建议,在形参里用指针形式,不要用数组形式。即使用了数组形式,编译器最终也会将其转换为指针形式,这样做虽然形式上能让人更好理解——传入函数的是数组,但是在理解上却加了一层枷锁,所以我建议形参中用指针的形式。

在此说一个众所周知的点——数组名是常量,不能被赋值。

 

例如:

int a[6]={ 0, 1, 2, 3, 4, 5};

a++;

a=a+1;

都是错误的,编译器不允许。

二、数组的指针

数组的指针是一个抽象的概念,需细琢,看几个例证:

 

1、 一维数组:

int array[2] = {0,1};

 

由此数组我们可以分解出array的元素(array[0]/array[1])、数组名array、以及数组的指针&array,看下图:

 

 例:

     1  #include <stdio.h>

     2

     3  int main(int argc, char **argv){

     4

     5          int n_array[5]={0, 1, 2, 3, 4};

     6          int i;

     7          int *p=NULL;

     8          for(i=0;i<5;i++){

     9                 printf("n_array[%d]=%d\n", i,n_array[i]);

    10          }

    11          p=n_array;

    12          if((n_array+1)==&n_array[1]){

    13                  printf("size of n_array= %lu, sizeof p = %lu\n",sizeof(n_array),sizeof(p));

    14          }else{

    15                 printf("(n_array+1)!=&n_array[1]\n");

    16          }

    17          if((&n_array+1)!=&&n_array[1]){//此处只能验证&n_array所指对象的大小不为sizeof(n_array[1]),在二维数组中验证

    18                  printf("&n_array !=&n_array[1]\n");

    19          }

    20          return 0;

21  }

 

执行结果:

n_array[0]=0

n_array[1]=1

n_array[2]=2

n_array[3]=3

n_array[4]=4

size of n_array = 20, sizeof p = 8

&n_array !=&n_array[1]

 

2、 二维数组:

Int array1[2][2] ={{0,1},{2,3}};

 

由此数组我们可以分解出array的元素(array[0][0]/array[0][1]/array[1][0]/ array[1][1])、降维后的数组array[0]和array[1]、降维后的数组的指针array、二维数组的指针&array,看下图:

 

 

例:

     1  #include <stdio.h>

     2

     3  int main(int argc, char **argv){

     4          int array_2D[2][2]={ { 0, 1}, { 2,3}};

     5          int *p=NULL;

     6          int **pp = NULL;

     7          int ***ppp = NULL;

     8          int i, j;

     9          for(i=0;i<2;i++){

    10                  for(j=0;j<2;j++){

    11                         printf("p[%d][%d]=%d\n", i, j, array_2D[i][j]);

    12                  }

    13          }

    14

    15          if((array_2D[0]+1) == &array_2D[0][1]){

    16                  printf("array[0] isnormal array{0, 1}'s first addr\n");

    17          }else{

    18                  printf("(array[0]+1) !=&array[0][1]\n");

    19          }

    20          if((array_2D+1)==&array_2D[1]){

    21                   printf("array is normalarray{0, 1}'s array point\n");

    22          }else{

    23                 printf("(array+1)!=&array[1]\n");

    24          }

    25         if((&array_2D+1)!=&array_2D[1]){

    26                  printf("(&array+1)!=&array[1]\n");

    27          }

    28          return 0;

29  }

 

执行结果:

p[0][0]=0

p[0][1]=1

p[1][0]=2

p[1][1]=3

array[0] is normal array{0, 1}'s first addr

array is normal array{0, 1}'s array point

(&array+1)!=&array[1]

 

这里简单介绍下指针数组:

指针数组:数组元素为指针类型。

 

三、 动态数组分配

还是那句话——在C语言中,只有指针算术和数组下标运算是等价的,指针和数组是不同的。指针运算和数组下标运算是等价的,根据这句话,就可以用malloc分配的内存来高效地模拟数组。

例如:

         #include <stdlib.h>

         int *array = (int*)malloc(10*sizeof(int));

当malloc申请内存成功以后,就可以像使用数组一样的使用array[i]。唯一的区别是sizeof不能给出分配空间的大小。

上面仅模拟了一维数组的动态分配,那多维数组如何动态分配呢?看几个例子:

例1:

     1  #include <stdio.h>

     2  #include <stdlib.h>

     3

     4  #define ARRAY_ROWS_MAX 10

     5  #define ARRAY_COLUMNS_MAX 8

     6

     7  int main(int argc, char **argv){

     8          int i,j;

     9          int **parray= (int **)malloc(ARRAY_ROWS_MAX*sizeof(int *));

    10

    11          if(parray){

    12

    13                 for(i=0;i<ARRAY_ROWS_MAX;i++){

    14                          parray[i] = (int *)malloc(ARRAY_COLUMNS_MAX*sizeof(int));

    15                          if(!parray){

    16                                 for(j=i-1;j>=0;j--)

    17                                         free(parray[j]);

    18                          }

    19                  }

    20          }else{

    21                  printf("alloc **parrayerror!\n");

    22                  return -1;

    23          }

    24          for(i=0;i<ARRAY_ROWS_MAX;i++){

    25                 for(j=0;j<ARRAY_COLUMNS_MAX;j++){

    26                          parray[i][j] = 1;

    27                  }

    28          }

    29

    30          for(i=0;i<ARRAY_ROWS_MAX;i++){

    31                 for(j=0;j<ARRAY_COLUMNS_MAX;j++){

    32                         printf("parray[%d][%d]=%d\n", i, j, parray[i][j]);

    33                  }

    34          }

    35          return 0;

36  }

 

执行结果:全为1

也可以让数组的内容连续,但在后来重新分配行的时候有些阻力,得使用二维数组的相关指针运算:

例2:

          1  #include <stdio.h>

     2 #include <stdlib.h>

     3

     4 #define ARRAY_ROWS_MAX 10

     5 #define ARRAY_COLUMNS_MAX 8

     6

     7 int main(int argc, char **argv){

     8

     9         int**parray1 = (int **)malloc(ARRAY_ROWS_MAX*sizeof(int *));

    10         int i, j;

    11         if(parray1){

    12                  parray1[0] = (int *)malloc(ARRAY_ROWS_MAX*ARRAY_COLUMNS_MAX*sizeof(int));

    13                  if(!parray1[0])

    14                          free(parray1);

    15                  for(i=0;i<ARRAY_ROWS_MAX*ARRAY_COLUMNS_MAX;i++){

    16                          parray1[i] =parray1[0]+i*ARRAY_COLUMNS_MAX;

    17                  }

    18         }else{

    19                  printf("alloc parry1error\n");

    20         }

    21

    22         for(i=0;i<ARRAY_ROWS_MAX;i++){

    23                 for(j=0;j<ARRAY_COLUMNS_MAX;j++){

    24                          parray1[i][j]=1;

    25                  }

    26         }

    27

    28         for(i=0;i<ARRAY_ROWS_MAX;i++){

    29                 for(j=0;j<ARRAY_COLUMNS_MAX;j++){

    30                         printf("parray1[%d][%d] = %d\n", i, j, parray1[i][j]);

    31                  }

    32         }

    33         return 0;

34  }

 

执行结果:全为1

以上两种情况,都可以用arrayx[i][j]来访问模拟的数组元素,其中他们的内存布局如下:


上面的两种分配方式对内存有两次间接访问,假如对程序执行时间要求非常高的系统,还可以用一个动态分配的一维数组来模拟二维数组,但是需要手动计算下标:

例3:

     1 #include <stdio.h>

     2  #include <stdlib.h>

     3

     4  #define ARRAY_ROWS_MAX 10

     5  #define ARRAY_COLUMNS_MAX 8

     6

     7  int main(int argc, char **argv){

     8          int i, j;

     9          int *parray2 = (int*)malloc(ARRAY_ROWS_MAX*ARRAY_COLUMNS_MAX*sizeof(int));

    10

    11          if(!parray2){

    12                  printf("alloc parray2error!\n");

    13                  return -1;

    14          }

    15          for(i=0;i<ARRAY_ROWS_MAX;i++){

    16                 for(j=0;j<ARRAY_COLUMNS_MAX;j++){

    17                         parray2[i*ARRAY_COLUMNS_MAX+j] = 1;

    18                  }

    19          }

    20

    21          for(i=0;i<ARRAY_ROWS_MAX;i++){

    22                 for(j=0;j<ARRAY_COLUMNS_MAX;j++){

    23                         printf("parray[%d][%d] = %d\n", i, j,parray2[i*ARRAY_COLUMNS_MAX+j]);

    24                  }

    25          }

    26          return 0;

    27  }

 

执行结果:全为1

 

还可以用一维数组指针来实现:

例4

     1  #include <stdio.h>

     2  #include <stdlib.h>

     3

     4  #define ARRAY_ROWS_MAX 10

     5  #define ARRAY_COLUMNS_MAX 8

     6

     7  int main(int argc, char **argv){

     8

     9          int i, j;

    10          int (*array)[ARRAY_COLUMNS_MAX]= (int (*)[ARRAY_COLUMNS_MAX])malloc(ARRAY_ROWS_MAX*sizeof(*array));

    11

    12          if(!array){

    13                  printf("alloc arrayerror!\n");

    14          }

    15

    16          for(i = 0; i<ARRAY_ROWS_MAX; i++){

    17                  for(j=0;j<ARRAY_COLUMNS_MAX; j++){

    18                          array[i][j] = 1;

    19                  }

    20          }

    21

    22          for(i = 0; i<ARRAY_ROWS_MAX; i++){

    23                  for(j=0; j<ARRAY_COLUMNS_MAX;j++){

    24                         printf("array[%d][%d]= %d\n", i, j, array[i][j]);

    25                  }

    26          }       

    27          return 0;

    28  }

          执行结果:全为1

最后,也可以用二维数组指针来实现:

例5

     1  #include <stdio.h>

     2  #include <stdlib.h>

     3

     4  #define ARRAY_ROWS_MAX 10

     5  #define ARRAY_COLUMNS_MAX 8

     6

     7  int main(int argc, char **argv){

     8          int i, j;

     9          int(*array2)[ARRAY_ROWS_MAX][ARRAY_COLUMNS_MAX] =(int (*)[ARRAY_ROWS_MAX][ARRAY_COLUMNS_MAX])malloc(sizeof(*array2));

    10

    11          if(!array2){

    12                  printf("alloc array2error!\n");

    13          }

    14          int *p = (int*)array2;

    15          for(i=0;i<ARRAY_ROWS_MAX;i++){

    16                 for(j=0;j<ARRAY_COLUMNS_MAX;j++){

    17                         p[i*ARRAY_COLUMNS_MAX+j] = 1;

    18                  }

    19          }

    20

    21

    22          for(i=0;i<ARRAY_ROWS_MAX;i++){

    23                  for(j=0;j<ARRAY_COLUMNS_MAX;j++){

    24                         printf("array2[%d][%d]=%d\n", i, j,  p[i*ARRAY_COLUMNS_MAX+j]);

    25                  }

    26          }

    27          return 0;

    28  }

 

执行结果:全为1

所有以上技术都可以扩展到3维或更高维数组,会更加复杂!

 

四、函数和多维数组

在“数组与指针的基本关系”我们提到“数组在传入函数时,函数接收到的不是数组本身,而是转化后的指针”,这个转化不能递归应用,二维数组被编译器转化为数组的指针,而不是指针的指针。如果向函数传递二维数组,函数的声明必须匹配,原因是数组的维度总是在编译时确定的常量,不能通过函数的其他参数来确定。

例:

 

     1  #include <stdio.h>

     2  #include <stdlib.h>

     3

     4  void func(int **test){

     5          int i, j;

     6          for(i=0;i<2;i++){

     7                  for(j=0;j<2;j++){

     8                         printf("test[%d][%d]=%d\n", i, j, test[i][j]);

     9                  }

    10          }

    11  }

    12

    13  int main(int argc, char **argv){

    14

    15          int array[2][2] = {{0,1},{2,3}};

    16          func(array);

    17          return 0;

18  }

 

编译会出warning:

 

当执行可执行文件汇报:Segmentationfault (core dumped),出现段错误了!

 

voidfunc(int **test)改为void func(int (*test)[2])或者void func(int a[][2])就OK。

 

五、数组的大小

char a[5] = {0, 1, 2,3, 4, 5};

int i =sizeof(a);

i的值为5,也即数组的字节长度为5。

但是在函数中呢?

void func(char a[5]){

         int i = sizeof(a);

         printf(“%d\n”,i);

}

此时打印出的i的值为4,也就是指针的大小,为什么不是数组的字节长度呢?原因见“数组与指针的基本关系”。

还有怎么判断数组中有多少个元素,方法如下:

int array[] ={1,2,3};

int i =sizeof(array)/sizeof(array[0]);

 

更复杂的结构数组的元素个数也是这样计算的。

结束!

1 0