第9章 指针(第二部分)

846阅读 0评论2011-01-11 A13433758072
分类:

  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. void crpx( int * , int * ) ;
  4. void cr( int * , int * , int ) ;
  5. void shuchu ( int * , int * );

  6. int main( void )
  7. {
  8.     int a[]={8,9,7,6,5,4,3,2,1,0};

  9.     crpx ( a , * (&a + 1 ) ) ;
  10.     shuchu( a , * (&a + 1 ) ) ;

  11.     system("PAUSE");
  12.     return 0;
  13. }
  14. //输出数组
  15. void shuchu ( int * tou , int *wei )
  16. {

  17.    printf("数组为:\n" );

  18.    while ( tou < wei )
  19.       {
  20.        printf(" %d " , * tou ++ );
  21.       }

  22.    putchar('\n');

  23. }
  24. //插入法排序
  25. //tou:数组开头;
  26. //wei: 数组结尾(指向最后元素之后下一对象)
  27. void crpx( int * tou , int *wei )
  28. {
  29.   //把数组划分为两部分,排好序部分和待插入元素部分
  30.   int *yx_tou = tou , *yx_wei = tou ,//有序部分:头,尾
  31.        *dcr_tou= yx_wei + 1 , *dcr_wei= wei ;//待插入部分:头,尾

  32.   //逐个把待插入元素插入有序部分
  33.   while ( dcr_tou < dcr_wei )
  34.      {
  35.       cr ( yx_tou , yx_wei , * dcr_tou ) ; //插入头一个
  36.       yx_wei ++ ;
  37.       dcr_tou ++ ;
  38.      }

  39. }

  40. //把待插入值插入数组
  41. //因为待插入值在有序数组之后,
  42. //所以总可以 *(yx_wei+1) = *(yx_wei)
  43. void cr( int * yx_tou , int * yx_wei , int crz )
  44.      {
  45.        if ( crz >= * yx_wei) //不用插入
  46.             {
  47.              return ;
  48.             }

  49.       *( yx_wei + 1 ) = * yx_wei ;//把末尾元素向后移动一个位置

  50.       if ( yx_tou == yx_wei) //有序数组只有一个元素
  51.            {
  52.            * yx_tou = crz ;
  53.            return ;
  54.            }
  55.       else
  56.            {
  57.             return cr ( yx_tou , yx_wei - 1 , crz ); //yx_wei - 1必须以
  58.                                                      //yx_wei > yx_tou为前提
  59.            }

  60. }



  1.  int a [2][3];
  1.  int (*)[3]


  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. int main( void )
  4. {
  5.     int a [2][3];

  6.     printf(" &a[0][0] = %p \n" , &a[0][0] );
  7.     printf(" a = %p , a + 1 = %p \n" , a , a + 1 );
  8.     printf(" sizeof ( * a ) = %d\n" , sizeof ( *a ) );
  9.     printf(" sizeof ( a[0] ) = %d\n" , sizeof ( a[0] ) );
  10.     printf(" sizeof ( int [3] ) = %d\n" , sizeof ( int [3] ) );

  11.     system("PAUSE");
  12.     return 0;
  13. }
    输出结果如图9-17所示。

  1. int (*p_a)[3];
  1. int *a_p[3];
  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. int main(void)
  4. {
  5.     int a [2][3];
  6.     int (*p) [2][3] = & a ;
  7.   
  8.     printf (" sizeof a = %u\n" , sizeof a);
  9.     printf (" a = %p ,&a = %p ,&a + 1 = %p\n" , a , &a , &a + 1 );
  10.     printf (" p = %p ,p + 1 = %p\n" , p , p + 1 );

  11.     system("PAUSE");
  12.     return 0;
  13. }
图9-18  高维数组名是内存
        



  1.  int qiuhe(int (*p)[6] , int hs , int i , int j , int m , int n );


  1.  int qiuhe(int *,int *);
  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. void shuru(int *,int *, int *,int *) ;
  4. int qiuhe(int *,int *);

  5. int main(void)
  6.        {
  7.           int a[5][6]={{2,3,4,5,6,7} ,
  8.                           {3,5,6,7,8,9} ,
  9.                           {4,2,1,6,3,7} ,
  10.                           {2,3,6,3,2,1} ,
  11.                           {1,3,5,2,6,2}
  12.                         };
  13.           int i,j,m,n;

  14.           shuru(&i,&j,&m,&n);
  15.           printf("从第%d行第%d列到第%d行第%d列元素的和为%d\n",\
  16.                    i , j , m , n , qiuhe(*(a+i)+j, a[m]+n) );

  17.           system("Pause");
  18.           return 0;
  19.        }

  20. void shuru (int *p_i,int *p_j,int *p_m,int *p_n)
  21.        {
  22.         printf("请输入i、j、m、n:\n");
  23.         scanf("%d%d%d%d", p_i , p_j, p_m, p_n);
  24.         return ;
  25.        }

  26. int qiuhe(int *p_beg,int *p_end)
  27.       {
  28.        int he=0;
  29.        do
  30.         {
  31.          he+=*p_beg;
  32.         }
  33.        while(p_beg++<p_end);
  34.        return he;
  35.      }


9.7  变量长度数组——VLA(C99)

9.7.1  简述
C99增加了一种新的数组—变量长度数组(VLA,Variable Length Array),这种数组允许在程序执行时才确定数组的大小。即这种数组的尺寸不再一定是整数类型常量表达式,可以是任一值大于0的整数类型表达式。
变量长度数组这种数据类型是C89和C++中都没有的数据类型,这种类型在许多问题中都非常有用,尤其是在数值处理方面,它在许多问题解决方案的描述上特别有力而且特别灵活。这可能是为了收编FORTRAN的一些成熟的算法,由于有了这种数据类型,现在许多程序可以写得更有通用性也更流利了。
下面代码是使用“变量长度数组”的一个例子。
程序代码9-24

  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. int main(void)
  4. {
  5.     int n,i;
  6.     scanf("%d",&n);
  7.     int a[n]; //只有支持C99标准的编译器才允许scanf();后定义变量

  8.     for( i = 0 ; i < n ; i ++ )
  9.         scanf("%d",&a[i]);

  10.     for(i=0;i<n;i++)
  11.         printf(" %d ",a[i]);

  12.     system ("Pause");
  13.     return 0;

  14. }
运行结果如图9-23所示。

图9-23  变量长度数组例一
特别需要说明的是,这种数组只可以是auto类别的局部变量。也就是说,只能在函数内部定义这种数组,而且不可以是static类别的。
既然变量长度数组只能是局部且必须是auto类别的,那么就一定是在某个复合语句模块中定义的。因此程序执行到这个模块的时候这个数组才获得自己的存储空间,而且,和所有auto类别的局部变量一样,程序一旦执行完它所在的复合语句模块,这个数组也就消失了—内存空间还给了操作系统。
有一种翻译把“变量长度数组”称为“可变长数组”。实际上这种数组的长度并不可变,它仅仅是用“变量”(Variable)来说明数组的长度而已。
一旦变量长度数组存在了(进行变量定义之后),就不可以再改变大小,直到最后消亡(离开作用区域)。并不会因为定义其长度的变量值的改变而改变自己的长度,但下次存在可能具有不同于上次的大小。下面代码运行的结果说明了这一点。
程序代码9-25

  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. int main( void )
  4. {
  5.     int n = 3 ;
  6.     double d[ n + 2 ];

  7.     printf("%u \n" , sizeof d );
  8.     n = 6;
  9.     printf("%u \n" , sizeof d );

  10.     for ( n = 3 ; n < 5 ; n ++ )
  11.         {
  12.          double d2[n];
  13.          printf("%u \n" , sizeof d2 );
  14.        }

  15.     system("PAUSE");
  16.     return 0;
  17. }
运行结果如图9-24所示。

图9-24  变量长度数组例二
以往的sizeof运算都是在编译期间完成的,而且sizeof里的表达式不求值,例如:
  1. int i;
  2. sizeof (i=2)
这个运算在编译时完成,“i”也不会被赋值为“2”,因为编译时“i”可能还不存在。
但是对于C99中的VLA,sizeof需要在程序执行时进行,此时sizeof的运算对象被求值。
C99对变量长度数组有一个限制,这种数据类型不可以作为结构体或联合体的成员。
9.7.2  变量修饰类型(Variably modified type)
变量长度数组的数组名在作为右值的时候同样是一个指向其起始元素的指针。在C99中也允许指向变量长度数组的指针。
同样,在C99中也存在指向变量长度数组的指针,这样的数据类型与变量长度数组统称为变量修饰类型(Variably modified type)。比如,对于变量长度数组

  1. int arr[m][n];
来说,“arr”的右值的类型就是指向“int [n]”类型的指针类型“int (*)[n]”,与之相对应的变量可以按照如下方式定义:
  1. int (*p)[n];
这里的“p”,和“arr” 一样都属于变量修饰类型。
下面代码在语法上演示了这种指针的用法。
程序代码9-26

  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. int main( void )
  4. {
  5.     int n = 4 ;
  6.     int d[n][n]; //一个方阵
  7.     int (*p)[n];

  8.     p = d ;

  9.     int i , j ;
  10.     for ( i = 0 ; i < n ; i ++ ) //形成一个单位方阵
  11.           {
  12.            for ( j = 0 ; j < n ; j ++)
  13.                  *(*(p+i)+j ) = (i==j) ;//主对角线上的元素都具有i==j的性质
  14.           }

  15.   for ( i = 0 ; i < n ; i ++ ) //输出方阵
  16.          {
  17.           for ( j = 0 ; j < n ; j ++)
  18.                printf( " %d " , *(*(p+i)+j ));
  19.           putchar('\n');
  20.          }

  21.     system("PAUSE");
  22.     return 0;
  23. }
程序输出如图9-25所示。

图9-25  变量修饰类型
9.7.3  变量长度数组与函数参数
了解了变量长度数组值的类型,就可以写出以变量长度数组作为实参的函数。以9.7.2小节中的“int d[n][n];”为例,考虑用一个函数判断“d” 是否构成一个单位矩阵。
首先,由于“d”作为右值的类型为“int (*)[n]”或“int [ ][n ]”,所以在函数原型中对应参数的类型为“int (*)[n]” 或“int [ ][n ]”。这表示一指向变量长度数组的指针。其中的“n”也可以写成其他标识符,如写成“int (*)[k]”。问题在于“n”或“k”这个标识符在使用前必须得到说明,这是C语言的一个原则。所以在此参数类型之前必须有另外一个参数—关于“k” 的类型说明。此外,数组第一维度的长度也必须作为参数。这样函数原型就应当写成:
“int shi_dwz(int k, int (*)[k] , int );”或“int shi_dwz(int k, int [][k] , int );”
与此相对应,函数的定义可以写成下面的形式
“int shi_dwz(int m, int (*p)[m] , int n){/*……*/}”或“int shi_dwz(int m, int p[][m] , int n){/*……*/}”
这样描述的函数定义及函数原型具有更广泛的适用范围,它可以接受任何二维数组作为参数,无论其是否为方阵,也无论其是否为变量长度数组。下面代码给出了这种函数的写法和测试。
程序代码9-27

  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. #define SHI 1
  4. #define FOU 0

  5. int shi_dwz(int k, int (*)[k] , int );

  6. int main( void )
  7. {
  8.     int n = 4 ;
  9.     int d[n][n]; //一个方阵
  10.     int (*p)[n];

  11.     p = d ;

  12.     int i , j ;
  13.     for ( i = 0 ; i < n ; i ++ ) //形成一个单位方阵
  14.          {
  15.           for ( j = 0 ; j < n ; j ++)
  16.               *(*(p+i)+j ) = (i==j) ;
  17.          }

  18.     printf("数组 d %s是方阵\n", shi_dwz(4, d , 4)?"":"不");

  19.     int a[3][3]
  20.         = { { 1 , 0 , 0},
  21.              { 0 , 1 , 0},
  22.              { 0 , 0 , 1}
  23.            } ;

  24.     printf("数组 a %s是方阵\n", shi_dwz(3, a , 3)?"":"不");

  25.     int b[2][3]
  26.         = { { 1 , 0 , 0 },
  27.              { 0 , 1 , 0 },
  28.            } ;

  29.     printf("数组 b %s是方阵\n", shi_dwz(3, a , 2)?"":"不");

  30.     system("PAUSE");
  31.     return 0;
  32. }

  33. //判断数组是否构成方阵
  34. int shi_dwz(int m, int (*p)[m] , int n)
  35. {
  36.     if ( m != n )
  37.          return FOU ;

  38.     int i , j ;
  39.     for ( i = 0 ; i < n ; i++ )
  40.         {
  41.         for ( j = 0 ; j < m ; j++ )
  42.                {
  43.                 if ( *(*(p+i)+j) != ( i==j ) )
  44.                     return FOU ;
  45.                }

  46.       }

  47.     return SHI ;

  48. }
测试结果如图9-26所示。
图9-26  变量长度数组与函数参数
按照C99标准,“int shi_dwz(int k, int (*)[k] , int );”这样的函数原型也可以不写出“[k]”里的那个“k”,而代之以“*”,这样也就无需对“k”的类型进行说明,即把函数原型写成:
  1. int shi_dwz(int , int (*)[*] , int );
但是这种格式目前Dev C++尚不支持。
  
9.8  数组类型的字面量(C99)
除了结构体类型的字面量,C99也允许数组类型的字面量。
由于数组通常是由多个数据组成的,所以也需要用一种方式把这些数据“组合”在一起。C语言通过运算符“{}”把数据组织在一起。此外还要表明这种数据的类型,C语言用类型转换运算实现。如:

  1. (int []){3,4}

  1. (int [2] ){3,4}
这表示一个由3、4组成的一个一维数组,然而这个数组没有自己的数组名。但它能进行和数组名能进行的相同的运算,比如作为一个实参。也就是说,这个数据具有和数组名同样的类型。
本质上“(){}”是C99中的一个运算符,在C99中,“(){}”是优先级最高的运算之一。
数组字面量也属于复合字面量(Compound literals)的一种,这种复合字面量很像一种具有数组类型的“常量”。但是从根本上来说,这种量并不同于“5”、“3.14”这样的常量。复合字面量最本质的特点是没有相应的标识符而是直接写出的,这就是“literal”的含义。
由于没有相应的标识符,所以数组类型的字面量最常见的用法要么是作为函数的实参、要么是把值赋给指针。下面的代码演示了数组类型复合字面量的用法。
程序代码9-28

  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. int qiu_he(int *, int );

  4. int main( void )
  5. {
  6.     int *p = (int [2]){3,4};

  7.     printf("(int [2]){3,4}的和为%d\n" ,
  8.             qiu_he ( p , sizeof (int [2]){3,4} / sizeof *(int [2]){3,4}) );

  9.     printf("(int [3]){4,5,6}的和为%d\n" , qiu_he ( (int [3]){4,5,6} ,
  10.             sizeof (int [3]){4,5,6} / sizeof *(int [3]){4,5,6}));

  11.     system("PAUSE");
  12.     return 0;
  13. }

  14. //求int数组各元素的和
  15. int qiu_he(int *p, int n)
  16. {
  17.     int i , he ;
  18.     for ( he = 0,i = 0 ; i < n ; i ++)
  19.      {
  20.            he += p[i];
  21.      }
  22.    return he ;
  23. }
运行结果如图9-27所示。

图9-27  数组类型的字面量
复合字面量也有自己的生存期,其生存期与对应的局部变量类似。
上一篇:第9章 指针(第一部分)
下一篇:第9章 指针(第三部分)