• 第八章 指针


    指针

      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("请输入您的字符串: ");
          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 ",p);  //%s的意思时从某个地址开始,输出到截至中间的部分,
                    //但是,因为get_usr_str()调用完成后,所有的形参和局部变量将全部释放,
                    //所以,此时(4000)开始的内存已经不存在,p为野指针,所以输出了乱值。
          p = get_usr_str1();  //得到了地址(5000)
          printf("%s ",p)    ;//因为常量文本的内存并没有释放,所以此时p不是野指针,%s的意思时从某个地址开始,输出到截至中间的部分
                    //常量文本最后是有的,所以正常输出了。
          }

  • 相关阅读:
    项目无法运行iPhone5模拟器
    多线程下载图片,滑动tableView崩溃--资源抢夺问题
    提醒事项 1. 冥想TX 2.下班路上听歌激励自己 3. 不戴眼镜 4. 困难任务拆解
    AutoLayout性能不如frame
    Tunnelblick 覆盖安装失败
    weakSelf 和 strongSelf
    动画
    4/16 近期状态
    知乎live 我的读书经验 总结
    C语言-第23课
  • 原文地址:https://www.cnblogs.com/hyt19911004/p/12392501.html
Copyright © 2020-2023  润新知