美文网首页
09-C语言指针

09-C语言指针

作者: 低头看云 | 来源:发表于2018-09-16 17:10 被阅读0次

    什么是指针

    • 指针

      • 存放地址的是指针
    • 指针变量

      • 指针是存放指针的变量
      • 指针变量和普通变量一样,都可以用来保存数据,当做形参,当做返回值;
      • 只不过指针变量只能保存内存地址
      • 简而言之,指针变量,就是专门用于保存内存地址的变量
    • 定义指针变量的格式

    int num = 6;  // 定义一个普通变量
    
    int *p;  // 定义一个指针变量
    // num变量的占用内存最小的那个地址存储到指针变量p中
    p = #
    
    
    • p是代表取出p中存储的值
    • &p是代表取出各自的内存地址
    int num = 6;
    int *p;
    p = #   // p指向num地址
    printf("&num = %p\n", &num);   // 0028FEBC
    // 代表取出p中存储的值
    printf("p = %p\n", p);         // 0028FEBC
    // &p和普通变量也一样,代表取出各自的内存地址
    printf("&p = %p\n", &p);       // 0028FEB8
    

    指针变量的初始化方法

    • 指针变量初始化的方法有两种:定义的同时进行初始化和先定义后初始化
    • 定义的同时进行初始化
    int a = 5;
    int *p = &a;
    
    • 先定义后初始化
    int num = 6;
    int *p;
    p = #
    
    - - 把指针初始化为NULL
    ```
    int *p = NULL;
    int *p = 0;
    ```
    
    • 给指针变量赋值时,指针变量前不要再加 "*" 星号;
    int num = 6;
    int *p;
    *p = # // 错误写法
    

    指针的大小和指向问题

    • 指标变量占用的内存大小都一样,在32位编译器中,占用4个字节;在64位编译器中,占用8个字节
    • 只要一个变量存储了另一个变量的内存地址,那么我们就说这个变量指向了另外一个变量
    • 只要一个变量存储另外一个变量的内存地址,我们就可以根据这个内存地址找到另外一个变量的内存,从而操作那一块内存
    ```
    int num = 6;
    int *p;
    p = #
    printf("修改前: num = %i\n", num); // 6
    *p = 88;
    printf("修改后: num = %i\n", num); // 88
    printf("修改后: *p = %i\n", *p);   // 88
    ```
    
    • 指针的注意点:
      • 1.指针只能保存地址,不能保存其它的值
      int *p = 1.1; // 注意千万不要这样写
      
      • 2.一个变量可以被多个指针指向
      int num = 6;
      int *p1;
      int *p2;
      p1 = #
      p2 = #
      printf("*p1 = %i\n", *p1);  // 6
      printf("*p2 = %i\n", *p2);  // 6
      
      • image
      • 3.指针的指向可以发生改变

      int num = 6;
      int *p = #  // p ---> num
      int value = 555;
      p = &value;   // p ---> value
      printf("%i\n", *p);
      
      • 4.定义指针变量时前面的数据类型,必须和将来要保存的变量前面的数据类型一致;

    指针为什么要有类型

    • 由于指针变量指向的是某一个变量占用存储空间的首地址
    • 所以在获取指向变量中存储数据的时候,指针是不知道要获取多少个字节的数据
    • 所以指针变量的作用:就是告诉指针,在获取指向变量的数据的时候,需要获取几个字节的数据
    int num = 888;
    //0000 0000 0000 0000 0010 0010 1011 1000
    //int *p;
    char *p;
    p = #
    printf("*p = %i\n", *p); // 888    120(改成char类型会发生数据错误)
    
    image

    小例子

    • 定义一个函数, 要求能够在函数中修改传入变量的值
    #include <stdio.h>
    // void change(int value);
    int main()
    {
        int num = 6;
        printf("调用前: num = %i\n", num);   // 6     6
        // change(num);
        change(&num);
        printf("调用后: num = %i\n", num);   // 6     666
        return 0;
    }
    
    //void change(int value){
    void change(int *value){
        *value = 666;
    }
    
    
    • 基本类型

      • 基本类型作为形参,在函数内修改形参,不会影响到函数外的实参;
    • image
    • 指针类型

    • image
    • 要定定义一个函数, 在函数中交换传入变量的值
    #include <stdio.h>
    void swap(int *num1, int *num2);
    int main()
    {
        // 需求: 要定定义一个函数, 在函数中交换传入变量的值
        int a = 4;
        int b = 6;
        printf("a = %i, b = %i\n" ,a, b);  // 4   6
        swap(&a, &b);
        printf("a = %i, b = %i\n", a, b);  // 6   4
        return 0;
    }
    
    // 传入的地址,则会修改实参
    void swap(int *num1, int *num2){
        int temp = *num1;
        *num1 = *num2;
        *num2 = temp;
    }
    
    
    image
    • 定义一个函数,可以同时返回两个变量的和, 差, 积, 商
    #include <stdio.h>
    void test(int a, int b, int *c, int *d,int *e,int *f);
    int main()
    {
        //要求定义一个函数,可以同时返回两个变量的和, 差, 积, 商
        int a = 10;
        int b = 20;
    
        int c, d, e, f;
        test(a, b, &c, &d, &e, &f);
        printf("和 = %i\n", c);
        printf("差 = %i\n", d);
        printf("积 = %i\n", e);
        printf("商 = %i\n", f);
    
        return 0;
    }
    
    // 注意点:return的作用是用于结束当前函数,return后面的代码执行不到;
    // 可以通过操作指针,实现地址的传递;来实现多个返回值;
    void test(int a, int b, int *c, int *d,int *e,int *f){
        *c = a + b;
        *d = a - b;
        *e = a * b;
        *f = a / b;
    }
    
    

    二级指针

    • 二级指针,就是指向指针的指针

    • 定义指针的规律

      • 1.将要指向变量的定义拷贝过来
      • 2.在拷贝过来的代码的类型和变量名称中间加上一颗星 *;
      • 3.修改变量名称
    int num;
    num = 6;
    
    // 一级指针
    int *p;
    p = &num;
    
    // 二级指针
    int **pp;
    pp = &p;
    
    
    • 访问二级指针
    int num;
    num = 6;
    
    // 一级指针
    int *p;
    p = &num;
    printf("p
    = %i\n", *p);
    
    // 二级指针
    int **pp;
    pp = &p;
    printf("pp = %i\n", **pp);   // 6
    
    
    image
    • 什么是多级指针

      • 多级指针就是指针中保存的有事其它指针的地址,我们就称为多级指针
    • 指针访问规律: 如果想通过多级指针获取某个变量的值,那么是几级指针,前面就写几颗星即可;

    指针和数组

    • 数组名称保存的就是数组占用内存最小的那个地址
    • 既然数组名称保存就是地址,而指针也是用于保存地址,所以指针也可以指向数据
    int ages[3] = {1, 3, 5};
    int *p = &ages;   // p = ages = &ages;
    
    • 结论:如果利用指针保存数组的地址之后,那么p = ages = &ages;
    • 指针遍历数组
    #include <stdio.h>
    
    int main()
    {
        int ages[3] = {1, 3, 5};
        for(int i = 0; i< 3; i++){
            printf("ages[%i] = %i\n", i, ages[i]);
        }
        printf("------------------------\n");
        int *p = &ages;
        for(int i = 0; i < 3; i++){
            //printf("agee[%i] = %i\n", i , *p++);
            printf("agee[%i] = %i\n", i , p[i]);
        }
    
        return 0;
    }
    
    • 操作数组
    int ages[3] = {1, 3, 5];
    int *p = &ages;
    // ages[0] = 8;
    p[0] = 88;
    
    • 访问数组的三种写法
    int ages[3] = {1, 3, 5};
    // 1.第一种
    printf("ages[0] = %i\n", ages[0]);  //   1
    // 2.第二种
    int *p = ages;
    printf("p[0] = %i\n", p[0]);   //    1
    // 3.第三种
    printf("0[p] = %i\n", 0[p]);   //   1
    
    • 指针可以进行加减法
    • 指针 +1, -1;
    • 应用场景: 一般都是用于指针数组中
    int nums[3] = {1, 3, 5};
    int *p = &nums;  // 等同于   int *p = nums;
    printf("*p = %i\n", *p);  // 1;
    printf("*(p + 1) = %i\n", *(p + 1)); //   3   
    printf("*(p + 2) = %i\n", *(p + 2)); //   5
    printf("*p = %i\n", *p);  // 1;
    // 此时p还是执行nums[0]的地址
    
    printf("*p = %i\n", *p++);  // 1;
    printf("*p = %i\n", *p++);  // 3;
    printf("*p = %i\n", *p);  // 5;
    printf("*p = %i\n", *(--p));  // 3
    // 指针先++,再取出里面的内容;
    
    • 注意点: 指针变量 +1是加多少, 加的就是指针变量类占用的字节数;
    • 如果指针的类型是int类型, 那么 +1 相当于 +4 个字节

    指针和字符串

    • 字符串的本质就是数组,所以指针也可以指向字符串
    char str1[] = {'c','w','w','\0'};
    char str2[10] = {'c','w','w'};
    char str3[] = "cww";
    //    printf("str1 = %s\n",str1);   // cww
    //    printf("str2 = %s\n",str2);   // cww
    //    printf("str3 = %s\n",str3);   // cww
    
    // 注意下面写法是错误的
    char *str4 = {'c','w','w','\0'};
    printf("str4 = %s\n",str4);   // 罢工
    
    • 利用数组和指针定义字符串的区别

      • 1.存储的位置不同
        • 如果是通过数组定义的字符串,那么存储在内存的栈中
        • 如果是通过指针定义的字符串,那么存储在内存的常量区中
      • 2.由于在内存中存储的位置不一样
        • 如果通过数组定义的字符串,我们是可以手动修改的
        • 但是通过指针定义的字符串,我们是不能手动修改的
        char str[] = "cww";
        char *str2 = "cww";
        str[1] = 'T';
        //printf("str = %s\n", str);  // cTw
        //str2[1] = 'T';
        printf("str2 = %s\n", str2);   // 罢工
        
        
      • 3.由于内存中的存储位置不一样
        • 如果是通过数组定义的字符串,每次定义都会重新开辟存储空间;
        • 如果是通过指针定义的字符串,重复定义不会重新开辟存储空间;
       // 存储地址不一样
      char str1[] = "cww";
      char str2[] = "cww";
      printf("str1 = %p\n",str1);  // 0028FEB4
      printf("str2 = %p\n",str2);  // ))28FEB0
      
      // 存储地址一样
      char *str3 = "cww";
      char *str4 = "cww";
      printf("str3 = %p\n",str3);   // 0040407A
      printf("str4 = %p\n",str4);   // 0040407A  
      
    • 注意点:

      • 接收字符串的时候,只能通过字符数组,不能通过指针
      char str1[10];
      char *str2;
      scanf("%s",str1); // 可以接收
      scanf("%s",str2);  // 不可以接收
      printf("str = %s\n",str1);
      
      • 将过去形参的数组类型修改为指针类型
      void test(int str1[]){
          被执行语句;
      }
      // 修改为
      void test(int *str1){
          被执行语句;
      }
      
      • 如果函数中返回的字符串是通过数组创建的,那么外界无法获取
      #include <stdio.h>
      char* test();
      int main()
      {
      
      
          char* res = test();
          printf("res = %s\n", res);  // 无法获取
      
          return 0;
      }
      
      char* test(){
          char str[] = "cww";
          return str;
      }
      
      
      • 如果函数中返回的字符串是通过指针创建的,那么外界可以获取
      #include <stdio.h>
      char* demo();
      int main()
      {
      
      
          char* res = demo();
          printf("res = %s\n",res); // 可以获取
      
          return 0;
      }
      
      char* demo(){
          char *str = "cww";
          return str;
      }
      
      
      image

    指向函数指针

    • 为什么指针可以指向一个函数
      • 函数作为一段程序,计算机也会给函数分配存储空间,既然函数会分配内存空间,
      • 所以函数也有自己的地址,所以指针变量也可以保存函数的地址
    - 数组名保存的就是数组的地址,同样,**函数名中保存的就是函数的地址**
        - 如果是数组,我们可以直接将数组名称赋值给一个指针变量
        - 如果是函数,我们也可以直接将函数名称赋值给一个指针变量
       
    ```
    #include <stdio.h>
    void test();
    int main()
    {
        test();
        printf("test = %p\n", test);  //  00401672
        printf("&test = %p\n", &test);  //  00401672
        return 0;
    }
    
    void test(){
        printf("test\n");
    }
    
    ``` 
    
    • 如何定义保存函数的指针变量

      • 1.将函数的声明拷贝过来
      • 2.在函数返回值和函数名称中间添加一个 * 星
      • 3.修改函数名称
      • 4.注意:需要将星号和变量名称用括号括起来
      #include <stdio.h>
      void test();
      int main()
      {
      
          void (*funcP)(); // 指向函数的指针
          // funcP = &test;  // 将函数的地址赋给指针
          // 等同于
          funcP = test;
          return 0;
      }
      // test函数
      void test(){
          printf("test\n");
      }
      
    • 如果一个指针指向了数组,那么访问数组有三种方式

      • 1.数组名称[索引];
      • 2.指针变量名称[索引];
      • 3.*(指针编码名称 + 索引);
      int nums[] = {1, 3, 5};
      int *p = &nums;
      // 数组名称[索引]; 
      printf("nums[1] = %i\n", nums[1]);
      
      // 指针变量名称[索引];
      printf("nums[1] = %i\n", p[1]);
      
      // *(指针编码名称 + 索引);
      printf("nums[1] = %i\n", *(p +1));
      
    • 如果一个指针指向了函数,那么也有多种访问方式

      • 1.函数名称();
      • 2.(*指针变量名称)();
      • 3.指针变量名称();
      #include <stdio.h>
      void test();
      int main()
      {
      
          // 第一种:函数名称(); 
          test();   
          
          void (*funcP)(); // 指向函数的指针
          funcP = &test;
          // 等同于
          // funcP = test;
          
          // 第二种:(*指针变量名称)();
          (*funcP)();   
          
          // 第三种:指针变量名称();
          funcP();     
          return 0;
      }
      
      void test(){
          printf("test\n");
      }
      
      
    • 小练习
    #include <stdio.h>
    void say();
    void sum(int a, int b);
    int minus(int a, int b);
    char* test();
    int main()
    {
        // 指针指向函数的练习
    
        // 1.
    //    void (*p1)();
    //    p1 = say;
    //    p1();
    
    
        // 2.
    //    void (*p2)(int , int);
    //    p2 = sum;
    //    p2(10, 20);
    
    
        // 3.
    //    int (*p3)(int, int);
    //    p3 = minus;
    //    int res = p3(30,20);
    //    printf("res = %i\n", res);
    
    
        // 4.
        char* (*p4)();
        p4 = test;
        char* res = p4();
        printf("res = %s\n", res);
    
        return 0;
    }
    
    
    void say(){
        printf("Hello World!\n");
    }
    
    void sum(int a, int b){
        printf("sum = %i\n",a + b);
    }
    
    int minus(int a, int b){
        return a-b;
    }
    
    
    char* test(){
        char *name = "cww";
        return name;
    }
    
    
    
    #include <stdio.h>
    int sum(int a , int b);
    int minus(int a, int b);
    int test(int num1, int num2, int (*func)(int ,int));
    int main()
    {
    
        int (*p1)(int ,int);
        p1 = sum;
        // p1 = &sum;  // 二者写法一致;
        int res1 = p1(10, 20);
        printf("res1 = %i\n", res1);   // 30
    
        p1 = &minus;
        int res2 = p1(10, 20);
        printf("res2 = %i\n", res2);   // -10
    
        int res3 = test(10, 20, sum);
        printf("res3 = %i\n", res3);   // 30
    
        // int res4 = test(10, 20, &minus);
        int res4 = test(10, 20, minus);
        printf("res4 = %i\n", res4);    // -10
    
        return 0;
    }
    
    /*
        指向函数的指针,作为函数的形参时,指针变量的名称, 就是形参的名称
        如果指向函数的指针作为函数的参数,那么这个可以称之为回调函数;
        这里相当于,给test函数传入了一个sum或minus函数
        然后又在test函数中调用了一个sum函数或者是minus函数
    */
    int test(int num1, int num2, int (*func)(int ,int)){
        return func(num1, num2);  
        // return (*func)(num1,num2);
    }
    
    int sum(int a , int b){
        return a + b;
    }
    
    int minus(int a, int b){
        return a -b;
    }
    
    
    image
    • 指向函数的指针,作为函数的形参时,指针变量的名称, 就是形参的名称
      如果指向函数的指针作为函数的参数,那么这个可以称之为回调函数;
      这里相当于,给test函数传入了一个sum或minus函数
      然后又在test函数中调用了一个sum函数或者是minus函数
    image

    相关文章

      网友评论

          本文标题:09-C语言指针

          本文链接:https://www.haomeiwen.com/subject/nurfnftx.html