• 08、C语言——指针


    指针

    一、指针的基本用法

      1、指针变量定义

        定义的格式:

            类型名 *指针变量名;

              int *p;
    

      2、指针变量的引用

        “&”取地址运算符,通过&运算符可以取出普通变量的地址【&内容变量=地址值】

        “*”指针运算符,*可以取出指针变量所指向的普通变量的值,(间接引用普通量)【*地址变量 = 内容值】

        指针变量的特殊意义:*p放在赋值号左边是写操作,如果放在赋值号右边是读操作

      3、注意事项

        1)可以通过赋值使一个指针变量“指向”某一普通变量(指针变量=&普通变量)

        2)在C语言中正确的做法是先让指针变量指向一个确定的存储单元后,再通过该指针变量引用它所指向的存储单元

             int *p;
            *p = 200;
            此操作很危险,指针变量定义且初始化后才可以使用

        3)变量名(普通变量、指针变量)都表示其存储单元内的值

        4)若指针变量p指向变量a,即将变量a的地址赋给了指针变量p

          int a=20,*p=&a;
          A、*p  <=>  a              等价都表示内容值
          B、  p  <=>  &a             等价都表示地址值 
          C、&*p  <=>  &a  <=>  p       等价都表示地址值
          D、*&a  <=>    *p   <=>   a       等价都表示内容值
          E、(*p)++   a++             (*p)++ 表达式的值是*p的内容,此时p指针后移
            (*p)--   a--
             ++(*p)  ++a   ++*p         ++(*p)表达式的值是*p的内容加1,同时p指针后移
             --(*p)  --a    --*p

        5)所有的指针变量在内存中分配的字节数相同

        6)指针变量必须定义且初始化之后才可以使用

        7)指针变量专门用存地址,禁止将一个整型值直接赋给一个指针变量

        8)若数组做为形参,则将数组名做指针变量来处理

            int fun(int a[10]) <=> int fun(int *a) = int fun(int a[])

    二、指向数组的指针变量

      1、指向数组元素的指针变量

        int a[10], *p;
        p=&a[1];

      2、指向一维数组的指针变量

        int a[10];
        int *p;
        p=a;
    

      3、注意事项

        1)在c语言中规定:数组名代表数组的首地址,而且是一个地址常量

        2)当指针变量指向数组中的某一个元素时,指针变量加1后指向数组的下一个元素,指针变量减1时指向数组中前一个元素

        3)当指针变量指向数组时,下标运算([])用于数组也可用于指针变量后

          int a[N],*p=a;
              p+i     <=>     a+i    <=>     &a[i]
            *(p+i)    <=> *(a+i)    <=>    a[i]    <=>    p[i]
             p++、 ++p    <=>     p+=1、 p=p+1
             p-- 、--p <=> p-=1 、p=p-1          *p++ <=> *(p++) 先标地址,取旧地址内容值,地址向下加          *++p <=> *(++p) 先把地址向下加1,把移动后的内容值取出来          (*p)++; ++(*p) <=> ++*p      指针没有移动,但是指针所指的内容值加1          (*p)--;--(*p) <=> --*p

       4)若两个指针变量指向同一个数组,则这两个指针变量可以进行大小比较

       5)在形参中的数组实际上是一个指针变量,并不是真正的数组,因为该“数组名”的值是可以改变的,而真正的数组名的值是不能改变的

       6)若形参是数组或指针变量,则在函数中可以通过该形参改变实参的值


    三、指向多维数组的指针变量

      1、指向多维数组元素的指针变量

          int a[10][10];
          int *p;
          p=a;
    

      2、指向由m个元素组成的一维数组的指针变量

        定义格式:

          类型名 (*指针变量名)[m];

            int (*p)[m];
    

      3、多维数组元素的两种指针变量的定义格式

       

            int *p;        列指针变量
            int (*p)[m];    行指针变量

      4、注意事项 

        1)只有列指针才是“真正”指向元素,即指向某一个元素的存储单元

        2)一维数组名表示的是列指针,二维数组名表示的行指针 

        3)注:若a是一个二维数组,则有:

           a+i是行指针,即指向的是一整行,若对它加1则指向下一行
           *(a+i)和a[i]一样,都是一个列指针即指向的是一个元素
           *(a+i)+j和a[i]+j一样,都表示元素a[i][j]的地址,即与&a[i][j]等价。
           *(*(a+i)+j)、*(a[i]+j)、(*(a+i))[j]和a[i][j]一样,都表示元素a[i][j]
    

        4)注:

          int w[2][3]
          *(w+1)[2]是元素,先计算[]后计算(),而[]是施加在()上
          *(w+1)[2]表示的元素的值是w[3][0]


    四、指向字符串的指针变量

      字符串常量——C语言对字符常量是按首地址处理字符串常量

        char str[]="abcd"    <=>   char str[]={"abcd"}
        char *p="abcd" ,不等价char *p={"abcd"}
        str="abcd"    不合法,常量不能放于赋值号左边
        p="abcd"     合法
    

      


    五、指向函数的指针变量

      1、定义格式:

          类型名 (*指针变量名)();

      2、注意事项:

        1)在定义指向函数的指针变量时,要注意有两个小括号必须要有,不需要定义形参

        2)单独的函数名代表该函数的首地址(函数的入口地址)

        3)函数的指针变量只能指向函数的入口处(函数的首地址)

        4)给指向函数的指针变量赋值时,只写函数名即可,不必写参数

    六、返回指针的函数

      定义格式:

        类型名 *函数名(形参列表)
        {

        };

    七、指针数组

      指针数组——一个数组的所有元素均为指针类型(地址)

      定义格式:

        类型名 *数组名[常量表达式];

          int *p[M];

      注意:

        1)要注意它和定义指向由m个元素组成的一维数组的指针变量之间的区别

          类型名 *数组名[常量表达式];

            int *p[M]; 

          类型名 (*指针名)[常量表达式m];

            int (*P)[M];

        2)它的每个元素都是一个指针类型(地址),即它的每个元素都相当于一个指针变量

     代码实现的过程:

    /*
    	2017年6月30日19:25:12
    	功能:指针函数和指向函数的指针
    */
    int add(int x, int y)
    {
     return x+y;											//return返回调用函数最终的计算结果
    }
    
    # include <stdio.h>
    int main(void)
    {
     int x, y;
     int total1, total2, total3;
     int (*p)(int x, int y);
     printf("Input x & y;");
     scanf("%d%d", &x, &y);
     p = add;											//p指向函数add;
     total1 = add(x, y);
     total2 = (*p)(x, y);								// *p必须带()否则会出错;
     total3 = p(x, y);									//p指向函数add;
     printf("1. %d + %d = %d
    ", x, y, total1);
     printf("2. %d + %d = %d
    ", x, y, total2);
     printf("3. %d + %d = %d
    ", x, y, total3);
    
    return 0;
    }
    
    /*
    总结:
      1.定义函数指针之后,必须首先将一函数名(代表该函数的入口地址)赋给函数指针,然后才能通过函数指针间接调用这个函数。
        函数指针变量名  = 函数名;
        在C语言中,函数名本身就是指向该函数的指针,因此可以用来函数指针进行赋值。函数名虽然是指针,但它是指针常量而不是指针变量,因而不能改变它的值。
      2.在利用函数指针来间接调用其所指向的函数时,该函数的定义必须存在,否则将出现错误。
      3.一个函数指针既可以指向用户自定义的函数,也可以指向C语言的标准函数。
    
    	在VC++6.0中显示的结果:
    	----------------------
    	Input x & y;11 11
    	1. 11 + 11 = 22
    	2. 11 + 11 = 22
    	3. 11 + 11 = 22
    	----------------------
    注意:
    	1、C语言中, 函数名本身就代表着该函数的入口地址。通过这个入口地址可以找到该 函数,该入口地址称为函数的指针。我们可以定义一个指针变量,使它的值等于函数的入口地址, 那么通过这个指针变量也可以调用此函数,这个指针变量称为指向函数的指针,简称为函数指针。
    	2、定义形式如下:
    	   数据类型 (*函数指针名) (形参);
    
    	3、指针函数与指向函数的指针是两个完全不同的概念,前者是函数,后者是指针,但是,指针函数的原型与函数指针的定义在格式却很相似,仅差一对圆括号。
    	   例如:
    		int (*fun) (int x, int y);
    		fun是一个指向函数的指针变量,所指函数返回值的类型为int类型,所指函数有两个整型参数。*fun两侧的圆括号不能省略,fun先与“*”结合,表明fun是指针变量;(*fun)后面的(int x, int y),表明指针fun指向函数,所指函数有两个整型参数。(*fun)前面的int,表明fun所指函数的返回值的类型为int类型。
    		int *fun(int x, int y);
    		fun是一个指针函数,该函数返回值的类型为“int *”类型,该函数有两个整型参数。fun先与其后的(int x, int y)结合,表明fun是一个函数,该函数有两个整型参数;fun前面的“int *”,表明函数fun的返回值是int类型的指针。	
    */
    

      

    八、指向指针的指针变量

      指向指针的指针变量——用来存放指针变量地址的指针变量

      定义格式:

         类型名 **指针变量名;

         int **p;
    

      

    九、空指针

      指针变量可以有空值,即指针变量不指向任何变量,不指向任何有用的存储单【在系统中已将null定义为0,即null的值为0】

        int *p=null;
        此时p的值为空指针,即p不指向任何有用的存储单元,尽管null的值为0。
        但我们不能认为p指向了地址为0的存储单元。
    

    十、小总结

      1、c语言有两种变量:

            变量(普通变量)存储内容值,常量都是内容值,只能存放在普通变量中;

            地址变量(指针变量)存储地址值;

       2、*的三种用法:

          乘法运算符

          定义指针变量的标志

          指针运算符

       3、三种运算符之间的关系   

          &与*互逆

          *与[]等价

          &与[]互逆

    代码分析解释:

    1、由于指针所指向的内容是地址, 所以指针的运算实际上是地址的运算。指针有它自己特有的运算规律,与一般的整数是有区别的。
    2、间接存取运算:
    				& 取地址运算符  * 取值运算符
    3、二者可以看作一对互逆运算符。在指针定义的时候“表示”“指向”, 在使用指针运算的时候,“*”表示取该指针变量所指向变量的值。
    	例如:
    	int n =2, * p;
    	p = &n;
    	&(*p) 等效于p, (因为p = &n,则*p = n, 则&(*p) = )其结果为(*p)的地址,即n的地址;
    	*(&n)等于n, 即地址(&n)所存放的值, 其结果就是2。
     
    4、在进行指针运算时,要注意p = &n 与 *p = n 这两个表达式的区别:
    	p = &n :是把变量n的地址赋给指针变量p,从而使p指向n,这时*p和n取值相同。
    	*p  = n :是将变量n的值赋给p当前所指向的变量。(因p指向变量n,实际上就是将变量n的值赋给其自身,如*p=3 是将3赋给n)
    	所以应严格区分p、 *p、和&p三者的区别:
    	p:是指针变量,其内容是地址量。
    	*p:是指针变量所指向的变量,其内容是变量的值。
    	&p:是指针变量本身所占据的存储地址。
    

      

       4、二维数组的关系图 

     

          5、指针类型的几种定义

          类型名 (*指针变量名)();  ——定义指向函数的指针变量

          类型名 普通变量名;        ——定义普通变量

          类型名 数组名[];        ——定义数组变量

          类型名 *指针变量名;       ——定义指针变量

          类型名 函数名()        ——定义用户自定义函数
          {....}

          类型名(*指针变量名)[M];     ——定义行指针变量

     指针的一些含义:

    # include <stdio.h>
    
    void main(void)
    {
    	 int n = 5, * p;
    	 p = &n;
    	 printf("%d
    ", &n);
    	 printf("%d
    ", p);     //p = &n;
    	 printf("%d
    ", &(*p)); //p = &n,则*p = n,即&(*p) = &n;
    	 printf("%d
    ", *(&p)); //【个人理解:&p表示指针变量p的地址,则*(&p)=p】
    	/* 以上四个表达式输出的是同样的值,都是n的地址值 */
    
    	 printf("%d
    ", &p);// &p表示指针变量p的地址;
    	/* 该表达式输出的是p变量的地址值 */
    
    	 printf("%d
    ", n);
    	 printf("%d
    ", *p); // *p = n;
    	 printf("%d
    ", *(&n)); *(&n) = n;
    	/* 以上三个表达式输出的是同样的值:5 */
    }
    

      

  • 相关阅读:
    1509 加长棒
    51Nod 1158 全是1的最大子矩阵
    P2953 [USACO09OPEN]牛的数字游戏Cow Digit Game
    P3384 【模板】树链剖分
    北京集训DAY3
    北京集训DAY2
    北京集训DAY1
    51Nod 1422 沙拉酱前缀 二分查找
    51Nod 1109 01组成的N的倍数
    51Nod 1043 幸运号码 数位DP
  • 原文地址:https://www.cnblogs.com/wxt19941024/p/6939567.html
Copyright © 2020-2023  润新知