C_基础_第七章 指针

指针

  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)在被调函数中可以通过 * 形参名=……的方式,修改主调函数相关变量的值。

 3.指针和数组

  指针和一维数组

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

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

      指针和下标的关系

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

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

      •  
        示例:
        #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 );
        
        }

         

 4、动态内存分配

  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 分配的静态内存;

5、指针应用。

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

    • 方法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  直至成伤  阅读(237)  评论(0)    收藏  举报