volcanol的工控博客
Email : lilinly225@126.com 索要资料加QQ 点击进入 或 点击左侧的资料分享专用帖

volcanol ---- View OF Linux Can Appreciate Nature OF Linux

天行健,君子以自强不息

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

       说到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[]之间差别可以参照:数组地址和指针进行复合运算 。

posted on 2011-06-05 10:46  volcanol  阅读(4063)  评论(6编辑  收藏  举报
volcanol ----View OF Linux Can Appreciate Nature OF Linux。