第八章 指针

指针

  1,指针
    指针,英文名是pointer,也叫地址,他是常量。他就是一个内存空间的首位置。
  2,指针变量,(地址变量)
    顾名思义,指针变量就是存放指针(地址)的变量。如果定义呢?
      short int * p = NULL;   //定义了一个指针变量P,并且付给初始值为零。
                // short int * 就是类型。表示的是存放short int型内存块的地址的。
                // p就是变量名。
                //付给初始值为零,零表示不是任何内存块的地址。或者称p为空指针

                //在32位系统下,所有的指针(地址)变量,都是四个字节。
                //我们把一个地址赋值给一个指针变量,我们就说,指针变量指向了该内存块(变量)
  3,*p的含义:这里的* 是运算符,取值运算符。
        *p的含义,根据p里的地址,找对应类型的内存块。
        如果p 的值为0,*p找不到内存,就会崩溃。
        如果p 是乱值, 就会崩溃。
        所以,未经初始化的变量是禁止使用的。
  4,p + n 的含义(地址加上一个整型数的意思)
    含义是:p这个地址向右移动n个存储单元(是p指向的内存单元),得到一个新的地址。
    p[n],含义是p地址后第n+1个存储单元(内存块)。
    由此得出,两个指针相减,等于间隔的内存块的个数。
    两个地址不能相加。
  5, 指针可以进行关系运算的。(在连续空间内比较才有意义)
    例如,> >=,<,<=,==,!=

 

1、指针的基本概念

  地址:(1)内存单元的编号

     (2)从零开始的非负整数

     (3)4G

  指针:(1)指针就是地址,地址就是指针

        (2)指针变量就是存放内存单元编号的变量,指针变量就是存放地址的变量

        (3)指针和指针变量是两个不同的概念  

     (4)指针的本质是一个操作受限的非负整数

  格式:  类型说明符 *指针变量名 ;       (指针变量的类型, 是指针所指向的变量的类型,  而不是自身的类型)

  示例: #include <stdio.h>

      int main() 

      {

         int  * p; //表示p变量存放int类型变量的地址

        p = & i;   //(1)p存放了i的地址,因此 p 指向 i;

            //(2)但 p 不是 i ,i也不是 p;更准确的说修改 p 的值不影响i的值,修改 i 的值不影响 p 的值;

            //(3)如果一个指针变量指向了某个普通变量,则*p指针变量,就完全等同于普通变量;

      }    

  指针变量的引用、

        取地址运算符── “&”  

        引用运算符 ── “*”

                             

        说明:     *p  对应类型内存块

  2、指针变量的运算

    (1)指针变量不能相加,不能相乘,不能相除

     (2)如果两个指针变量指向的是同一块连续空间中的不同存储单元,则这两个指针变量可以相减。

  3.指针与函数

  通过被调函数修改主调函数变量的值,实现函数返回一个以上的值;

    (1)实参必须是该普通变量的地址

    (2)形参必须是指针变量

    (3)在被调函数中可以通过 * 形参名=……的方式,修改主调函数相关变量的值。

 

   示例:

      #include <stdio.h>

       void func ( inr * p , int * q )

      {

        int temp =0;

        temp = * p ;

        * p = * q ;

        * q = temp ;

      }

      int main() 

      {

         int  a = 1; 

         int b =2;

        func ( & a, & b );

        printf("%d%d".a ,b );

      }

 3.指针和数组

  指针和一维数组

      (1)一维数组名是个指针常量

      (2)它存放的是一维数组第一个元素的地址 

      指针和下标的关系

       (1)如果 p 是个指针变量,则 p [ i ] 永远等价于 * (p + i) 

          (2)确定一个一维数组需要两个参数(数组地址和参数个数)

 

 动态内存分配

  1、传统数组缺点

     (1)数组长度必须事先指定,且只能是常整数,不能是变量

    (2)传统形式定义的数组,该数组的内存程序员无法手动释放,直到函数运行结束,系统自动释放。

    (3)数组长度一旦定义,其长度就不能更改;

    (4)传统方式定义的数组不能跨函数使用

    动态分配内存可以很好地解决传统数组的缺点

  2、malloc函数(memory  allocate)

    int * p =( int  * ) malloc ( sizeof ( int ));

    (1)必须添加头文件 malloc . h

    (2)只有一个形参,并且形参为正数

    (3)返回值为分配内存的第一个字节的地址;

    (4)free(变量)手动释放分配的空间

    (5)free能释放malloc分配的动态内存,不能释放 int * p 分配的静态内存;

指针应用。

  (1)我现在想要模块化编程,写函数,写一个函数来实现两个数的交换。

  方法一

      #include <stdio.h>
      void swap(int a,int b)
      {
        int t = 0;
        t = a;
        a = b;
        b = t;
      }
      void main()
      {
        int a = 10;
        int b = 5;
        swap(a,b);
        printf("%d%d",a,b);
      }

上面的传递方式,我们叫他数值传递,因为值传递实参和形参是不同的内存,所以无法改变主调函数变量的值。
结论,值传递不可能改变主调函数变量的值。
  方法二
      include <stdio.h>
      void swap(int * a,int * b)//调用时后,a的值为(4000)b的值为(5000)
      {
        int t = 0;
        t = *a;  //*a ,根据a里面的地址(4000),找对应int型内存块。所以,此时*a(被调) 等价于a(主调)
        *a = *b  //*b ,根据b里面的地址(5000),找对应int型内存块。所以,此时*b(被调) 等价于b(主调)
        *b = t;  //由此可见,通过*的运算,可以找到另外一个栈帧中的变量,隔山打牛的方式间接修改主调函数变量的值。
      }
      void main()
      {
        int a = 10;
        int b = 5;
        swap(&a,&b);  //假设 a的地址是(4000),b的地址是(5000)
        printf("%d%d",a,b);
        }
 方法三
  #include <stdio.h>
  void swap(int * a,int * b)//调用时后,a的值为(4000)//b的值为(5000)
  {
    int * t = 0;
    t = a;  //就时把a的数值赋值给变量t了。
    a = b;  //就时把b的数值赋值给变量a了。
    b = t;  //所以这个程序只是交换了a和b的地址而已。函数调用后,函数释放内存,形参和局部变量分配的空间也就释放了。
  }
  void main()
  {
    int a = 10;
    int b = 5;
    swap(&a,&b);//假设 a的地址是(4000),b的地址是(5000)
    printf("%d%d",a,b);
   }

方法四

    #include <stdio.h>
    void swap(int * a,int * b)//调用时后,a的值为(4000)b的值为(5000)
    {
      int * t = 0;
      *t = *a;  //崩溃
      *a = *b;//
      *b = *t;  //所以这个程序只是交换了a和b的地址而已。函数调用后,函数释放内存,形参和局部变量分配的空间也就释放了。
    }
    void main()
    {
      int a = 10;
      int b = 5;
      swap(&a,&b);//假设 a的地址是(4000),b的地址是(5000)
      printf("%d%d",a,b);
      }
三种方法的比较,有的方法可以实现,有的方法实现不了,为什么?

  (2)第二题

    char * get_usr_str()
    {
      char mystr[30] = {0};//局部变量,数组。mystr是数组名,他是内存块(mystr[0])的首地址。我们可以假设这个地址是(4000).
      printf("请输入您的字符串:\n");
      scanf("%s",mystr);//%s 按字符串的方式来读取,读取内容,放到mystr(4000)地址开始的内存块中。
      return mystr;//把(4000)这个地址值返回了。
    }
    char * get_usr_str1()
    {
      char * p = "ilovechina";//常量文本"ilovechina",分配到常量文本去,常量文本区分配
                //分配的内存,将再程序结束后,由操作系统统一释放。同时,按C语言
                //语法规定,字符串赋值给指针变量,实际上是把字符串的首地址赋值
                //给指针变量了,例如(5000),那么p的值就为(5000)
                //额外我们说一句,字符串作为函数实参,也是把字符串的首地址作为实参
                //赋值给形参了。所以我们可以认为,字符串常量的返回值类型为char *
      return p;//把(5000)地址返回。
    }
    void main()
    {
      char * p = NULL;   //定义了指针变量。
      p = get_usr_str();  //调用完成后,p的地址被赋值给函数的返回值,(4000)了。此时p就是4000.
      printf("%s\n",p);  //%s的意思时从某个地址开始,输出到截至\0中间的部分,
                //但是,因为get_usr_str()调用完成后,所有的形参和局部变量将全部释放,
                //所以,此时(4000)开始的内存已经不存在,p为野指针,所以输出了乱值。
      p = get_usr_str1();  //得到了地址(5000)
      printf("%s\n",p)    ;//因为常量文本的内存并没有释放,所以此时p不是野指针,%s的意思时从某个地址开始,输出到截至\0中间的部分
                //常量文本最后是有\0的,所以正常输出了。
      }

 

posted @ 2020-03-01 22:08  直至成伤  阅读(217)  评论(0编辑  收藏  举报