zoukankan      html  css  js  c++  java
  • C语言学习趣事_关于C语言中复杂类型定义

           说到C语言, 很多人都是又爱又恨啊,既感到用C语言给了程序员极大的开放度和自由度,同时又对C语言的灵活性和高难度性。

           就目前中国教育做法来说吧,估计大部分高校给学生选的入门级语言就是C语言, 然而经过大学几年的学习,大部分的学生也只能做到写个“HelloWord” 这样的代码。即便是计算机专业的毕业生,在离开学校后,大部分也是对C语言的掌握也只是停留在简单的应用,更不用说非计算机专业的学生了, 就像我这样非计算机专业毕业的,到现在也不会用C语言编写一个具有实际应用意义的程序。

            估计C语言中最难让人摆平的估计要算是指针了, 不但难以捉摸,同时又非常复杂。尤其是当具有复杂的数据类型定义的时候。

    1、指针

         何谓指针,这个问题估计不需要说明了。从硬件角度来看,指针应该指的是CPU地址总线上呈现的电平状态的数字化表示,估计大家都知道经典8051中的寻址过程, 通过地址总线选择需要操作的存储地址;在8051中我们知道共有16根地址总线, 因此具有2^16方的可寻址空间,就是具有64K的寻址空间。当所有地址总线呈现低电平时选择的是0000_0000_0000_0000,即0000H的地址;而当地址总线全部呈现高电平状态则选择的是1111_1111_1111_1111,即FFFFH的地址。这个同样适合8086架构下的寻址, 如果用汇编语言编写程序,就可以直接指定要操作的地址,或者说可以直接寻址地址。

    2、C语言中的指针

        C语言高效的一个原因就在于可以直接对地址进行操作,虽然不如汇编语言那样的直接,但是相对于其他一些语言例如VB等语言来说,C的指针操作已经非常“高级”了。C语言的发明者真够神的, 发明了指针这样难以驾驭的指针,但是C语言中指针的定义则非常的简单。

    3、C语言中指针类型定义

         定义语法:

         指针指向的基类型   *  指针标识符

         例如:   int  * pValue;   这样就定义了一个可以指向int类型变量的指针,哈哈,还真神奇,这样就可以控制硬件的连线上的电平了,

    4、指针用法

    测试代码:

    Exp1:

    #include <stdio.h>

    #define  PINT int *

    int max(int x, int y)
    {
       return x>y ? x : y;
    }

    int main(int argc, char **argv)
    {
      int pTest;
      PINT pToInt;
      int (*p)(int, int);
      pToInt=&pTest;
      pTest=100;
      p=max;
      printf("%d, %d, %d", *p,max,*pToInt);
      getch();
    }

    这个地方发现一个编译上的差距: 在WinTC上编译 *pToInt 输出的是 7, 而在VC 6.0中输出的则是预想的100; 为什么呢? 目前没有搞明白,哎..........

    同时输出的*p== max; 这段测试代码用到了一个比较特殊的指针定义,函数指针, 从输出来看与实际预想的一样。如上图所示可以知道WinTC、VC6对地址的解释不一致。唯一可以解释的就是在Win32中地址是平坦的即flat的寻址方式, 而在Win16或者Dos上就存在很多种寻址方式,比方说tiny,small等方式造成这样的,因为我在WinTC中测试的结果是 sizeof int = 2; 而在VC6.0中测试的结果是 sizeof int = 4,估计是因为这个原因导致上面的输出不一致。

         在函数参数中使用指针:

          int main(int argc, char **argv)   // main函数的参数中就使用指针,并且是指针的指针。

          等价版本的main函数原型

          int  main(int argc,char *argv[])

          在函数参数中传递函数指针

           int get_max( int x, int y, int (* p)(int x,int y) )

           {

                   return   (*p)(x,y);

            }

    结合上面的程序可以实现以下程序代码:

    #include <stdio.h>

    #define PINT int *

    int max(int x,int y)
    {
        return x>y?x:y;
    }
    int get_max(int x, int y,int (*p)(int x, int y))
    {
          return (*p)(x,y);
    }
    int main(int argc,char **argv)
    {
        int (*p)(int,int),
            test;
        PINT pToInt;
        p=max;
        test=get_max(10,20,*p);
        pToInt=&test;

        printf("%d, %d, %d",*p,max,*pToInt);

        getchar();
    }

     5、复杂指针定义:

    指针常量和常量指针:

         int   const * p;  // 定义一个指向常量的指针,  这个指针可以随意改变指向

         int   * const p; //定义一个指针常量, 这个指针只能指向一个变量,并且指向后不能在改变

         const int * const p; //定义一个指向常量的指针常量, 指针变量本身的值不可修改,并且指针指向的变量也不能被修改。

        例如:
        int * const pconst=&test;   //这里定义的pconst指针就不能再指向别的整型变量
        const int constvalue=50;    //定义一个整型常量, 等价于   int const constvalue
        int const *constvar=&constvalue;   //定义一个指向整型常量的指针, 指针指向的变量值不可修改,但是指针指向可以更改
        const int * const constpvar=&constvalue;  //定义一个指向整型常量的指针, 指针的指向不可修改。

        这类指针定义的一个简单的阅读方法就看: const修饰的是什么,  当其修饰数据类型的时候则定义的是数据类型的变量不能修改;

                                                               当修饰的是指针变量的时候则指针的指向不可改变。

        从上面的实例可以看出: 当没有指针存在的时候,const 的位置不会影响变量的使用,但是也可以根据其修饰的对象来理解。

    指针数组和数组指针

        int  *p[];

        int   (*p)[]; 

        这两类指针的定义着实非常令人纠结啊,  到底怎么解释和理解呢? 一团雾水啊...........

        首先看第一个: int  *p[ ];

        如上定义:  p 的左右各有一个运算符, *和[]; 指针运算符和数组运算符, 在C规范里面, 数组运算符的优先级别高于指针运算符。

        在这里同样可以利用运算符的优先级来理解这个指针, 

        因为[ ]的优先级高于 * ,引起p应该先和[ ]结合,这里就是可以看出,p是一个数组, 然后p再与* 结合,可以看出p是一个指针,最后看数据基类型,p的数据基类型是int型的;综合上面的描述可以知道: p被定义为一个存储int型指针的数组。 即p是一个数组,其数组元素的类型是int型指针。

       例如:  int  px;

                 int   py;

                 int   pz;

                 int *p[3];

            则可以有:

                p[0]=&px;

                p[1]=&py;

                p[2]=&pz;

           如果要引用其指向的变量的内容的话,可以这样使用:   int  sizex=*p[0];   这就是指针数组, 就是说数组元素全是指针。

           接下来看第二个:

           int (*p)[];

           同样可以利用优先级别来理解:  ()和[ ]具有相同的优先级, 因此 p 是一个指针, 然后再用 [ ]来修饰p; 则可以看出p将指向一个数组类型数据,这就是说 int (*p)[]是指向int型数组的指针。这个指针不能指向别的数组。

           例如:

           int  iArray[4][5];
           int  (*pArray)[5];
           pArray=iArray;    //这样可以编译成功, 因为pArray的类型是 :  int (*) [] ;  而 iArray 的类型是  int [4][5]; 可以进行数据类型的转换

           但是如果:

           pArray=&iArray;  //编译不成功,为什么呢? 因为pArray的类型是 :  int (*) [] ;  而&iArray 的类型是  int *[][];很显然数据类型不一样

          如果:

          pArray=&iArray[0];  // 编译成功。

           我们知道在二维数组中, 可以这样理解:其行元素相当于指针,即 iArray[0]、 iArray[1]、 iArray[2]、iArray[3], 但是其存储的并不是指针,其存储的是一个具有5个元素数组的首地址。但是需要这样才能 iArray=&iArray[0];  (这里二维数组的首地址与 iArray[0] 的地址相同 )。

         对于数组指针的理解,可以将变量去除后然后进行剥离得出其数据类型然后进行理解。例如:

         pArray: int (*)[];

         &iArray[0]: int (*)[ ];

          特殊的引用方式:

         int  iArray[4];
         int  (*pArray)[4]=NULL;    //指定义不初始化同样可以,但是为了防止出现游离指针,最好用NULL初始化;
         pArray=&iArray;     // 这个编译成功 

    指向函数的数组指针

         int (*a[10])(int); // 这个定义一个数组,数组共有10个元素,每个元素存储一个指向 int (*)(int )函数的指针, 同样利用优先级来理解。

    指针的指针   

           int **pToPoint;

           int *pToInt;

           int  age;

           pToAge=&age;

           PToPoint=&pToAge;

           通常这个应用于数组和字符串的处理, 可以见两种main函数原型的定义。

           哎..........虽然写了这么多但是对于这个C语言中的指针认识还是不够,尤其是数组指针那一块,估计需要慢慢琢磨才能真正的理解,

           同时对于 **P和*p[]之间差别可以参照:数组地址和指针进行复合运算 。

  • 相关阅读:
    Java多线程简介
    Java同步简介
    java enum的用法详解
    Instrumentation(3)
    持久化类的三种实例状态
    依赖注入和控制反转
    事务的4个要素及其工作原理
    mysql创建表与索引
    SpringAOP所支持的AspectJ切点指示器
    使用Spring的命名空间p装配属性-摘自《Spring实战(第3版)》
  • 原文地址:https://www.cnblogs.com/volcanol/p/2073042.html
Copyright © 2011-2022 走看看