zoukankan      html  css  js  c++  java
  • C语言-指针

    无指针,不自由

    --1-- 为什么要使用指针
    1.1 指针的基本概念
    1.2 使用指针的好处
    1.3 变量的存储方式
    --2-- 指针变量
    2.1 指针变量
    2.2 定义一个指针变量
    2.3 指针变量的初始化方法
    2.4 使用*获取指针对应存储区域的内容
    --3-- 存储细节
    3.1 变量及指针变量
    --4-- 常见的应用场景
    4.1 指针常见的应用场景
    --5-- 多级指针介绍
    5.1 二级指针
    5.2 多级指针介绍
    ----------------------------
     
     
     

    【写在开头:】

    生活中的指针:

    没错,就是一个门牌号。

    指针是C语言中最重要的内容之一。

    为什么要使用指针?

    如果你想要查找一篇资料,给你一本厚重的百科全书,而你只需要其中第3001页的内容。

    那么,3001页,就是指针。如果没有这个指针...好吧,我去旁边哭会儿...

    --1-- 为什么要使用指针

    1.1 指针的基本概念

    内存单元的编号也叫做地址,根据内存单元的编号或地址就就可以找到所需的内存单元。

    所以通常也把这个地址称为指针

    1.2 使用指针的好处

    1)为函数提供修改调用变量的灵活手段

    2)让函数有多个返回值

    3)可以改善程序的效率

      在数据传递时,如果数据块较大(比如数据缓冲区或比较大的结构),这时就可以使用指针传递地址而不是实际数据,既提高传输速度,又节省了大量的内存

    4)为动态数据结构(如二叉数,链表等)提供支持

    1.3 变量的存储方式

    变量存取的方式有两种:直接存取和间接存取

    1)直接存取:变量的赋值和取值

    2)间接存取:通过指针(地址)间接操作完成

    指针最大的作用之一就是通过地址来操作(存取)变量、数组等

    --2-- 指针变量

    2.1 指针变量

    在C语言中,允许用一个变量来存放指针,这个变量称为指针变量。

    指针变量存放的是一个地址,是一个变量

    2.2 定义一个指针变量

    指针变量的定义包括三个内容:

    1)指针类型说明,即定义变量为一个指针变量

    2)指针变量名

    3)变量值(地址)


    一般形式:

    类型说明符   *变量名 = 地址;

            int *p;  //定义了一个指针变量,变量名是p int表示p指向的是一个int类型的变量空间
            char *p1;   //变量名是p1 char表示p1指向的是一个char类型的变量空间

    其中

      类型说明符表示本指针变量所指向的变量的数据类型

      *表示这是一个指针变量

      变量名即为定义的指针变量名

     注意:

    1)在定义指针时,*表示定义的变量是一个指针变量(如果没有*就和普通变量没有区别了)

    2)指针变量能用来存放数组或字符之类?-->不能(存储的是地址)

    3)一个类型的指针只能指向同类型的变量,不能指向其他类型的变量

    4)指针变量,归根结底还是一个变量,也有全局和局部之分

    2.3 指针变量的初始化方法

    指针变量应该用地址初始化

    两种初始化的方式:

    定义的同时初始化、先定义后初始化

    1)定义的同时初始化

        //完全初始化
        int a = 3;
        int b = 4;
        int *P = &a; //用a的地址初始化p这个指针变量(也称为p指向了a))
        int *p1 = &a, *p2 = &a;  //p1,p2都指向了a 

    2)先定义后初始化

        //部分初始化
        int *p3 = &b, *p4; //定义了两个指针变量 p3和p4 p4未初始化
        p4 = &b;

    2.4 使用*获取指针对应存储区域的内容

    指针变量同普通变量一样,使用之前不仅要定义说明,而且必须赋予其具体的值。

    未经赋值的指针变量不能使用,否则将造成系统混乱,甚至死机(野指针)。指针变量的赋值只能赋予地址,不能赋予任何其他的数据,否则将引起错误。

    C语言中,变量的地址是由编译系统分配的,对用户完全透明,用户事先并不知道变量的具体地址(但可以知道区域位置)。

    两个相关的运算符:

      &:取地址运算符;

      *:指针运算符(或称“间接访问”运算符);

    C语言提供了地址运算符"&"来表示变量的地址,一般形式为:&变量名;

    获取指针变量指向的存储空间的内容:*指针变量名

    *运算符和&运算符恰好相反。&运算符接收一个数据,然后告诉你这个数据保存在哪里;*运算符接收一个地址,然后告诉你这个地址中保存的是什么数据。

    因为指针有时也叫引用,所以*运算符也可以描述成对指针进行解引用。

            int a = 3;
            int b = 4;
            int *p1 = &a;
            int *p2 = &b;
            
            //*指针变量  作用:获取指针变量指向的内存空间的内容(值)再赋值给value
            int value = *p1;  //3
            *p1 = 100;   //改变空间的值
            value = *p2;  //4
            printf("*p1 = %d
    ", *p1); //100
            printf("value = %d
    ",value );   //4

    思考

    定义如下指针

            float *pointer_3;
            char *pointer_4;
            int a,b;
            int *pointer_1 = &a, *pointer_2;

    判断下面的操作是否合法?

            *pointer_1 = &a;
            pointer_3 = 2000;
            pointer_3 = &a;
            pointer_1 = &a;
            pointer_2 = pointer_4;

    分析:

            *pointer_1 = &a;  //错; *pointer_1是一个解引用,是一个值,而非地址
            pointer_3 = 2000;  //错;pointer_3指针并未初始化,且需赋值的是一个地址
            pointer_3 = &a;  //错; 应该赋值一个float类型的空间地址给pointer_3
            pointer_1 = &a;  //合法
            pointer_2 = pointer_4;   //错; 类型不同

    --3-- 存储细节

    3.1 变量及指针变量存储细节

    每当声明一个变量,计算机都会在存储器中某个地方为它创建空间。如果在函数(例如main()函数)中声明变量,计算机会把它保存在一个叫栈(Stack)的存储器区段中;如果你在函数以外的地方声明变量,计算机则会把它保存在存储器的全局量段(Globals)。

    int y = 1; //位于全局量段 地址:1000 000
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
    
            int x = 4; //位于栈区 地址:4100 000
        }
        return 0;
    }

     有如下变量

    int i = 200;

    int *p = &i;

    此时p指向了变量i的首地址&i

    --4-- 常见的应用场景

    4.1 指针常见的应用场景

    这样的一种场景很常见,参数在函数间进行传递时,要通过被调函数改变主调函数中实参变量的值。

    如:交换两个变量的函数,如果不使用指针,则一般用的是临时变量等方法。

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
    
            int x = 3, y = 4, temp;
            //临时变量交换两个变量的值
            temp = x;
            x = y;
            y = temp;
            printf("x = %d, y = %d
    ", x, y);
        }
        return 0;
    } 

    但上面这样的写法是不标准的,一般需要将其封装成自定义方法才好(原谅我这里的说法不准确,“封装”是面向对象语言的特性)。

    那么如果封装(习惯这样叫了...)成自定义方法之后,如果实参传递的是一个变量,那么主调函数中变量的值不会改变,因为形参实际上是又开辟了一个新的内存空间,所以在自定义函数中并不能修改实参的值:

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            void swap(int x, int y); //函数声明
            
            int x = 3, y = 4;
            swap(x, y); //变量作为实参并不能修改实参空间中的值
            printf("x = %d, y = %d
    ", x, y); //未改变 x = 3, y = 4
        }
        return 0;
    }
    
    /**
     *  测试方法,交换变量的值
     *
     *  @param x 变量1
     *  @param y 变量2
     */
    void swap(int x, int y){
        //临时变量交换两个变量的值
        int temp = x;
        x = y;
        y = temp;
    }

     要实现上面设定情景,就只能传递指针地址:

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            void swap(int *x, int *y); //函数声明
            
            int x = 3, y = 4;
            swap(&x, &y); //将地址作为形参就可以交换此处x和y的值
            printf("x = %d, y = %d
    ", x, y); //x = 4, y = 3
        }
        return 0;
    }
    
    /**
     *  交换两个变量的值
     *
     *  @param x 地址1
     *  @param y 地址2
     */
    void swap(int *x, int *y){
        //临时变量交换两个变量的值
        int temp = *x;
        *x = *y;
        *y = temp;
    }

    --5-- 多级指针介绍

    5.1 二级指针

    如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量,也称为“二级指针”。

    通过指针访问变量称为间接访问,由于指针变量直接指向变量,所以称为“一级指针”。而如果通过指向指针的指针变量来访问变量则构成了“二级指针”。

            int a = 5;
            int *p = &a;
            printf("%p
    ",&a); //a的地址
            printf("%p
    ",p); //指针p中存储的是a的地址
            printf("-----
    ");
            //****二级指针
            int **p1 = &p; //p1中存储的是一个指针
            printf("p = %p
    ", &p); //指针p的地址
            printf("p1 --> %p
    ", p1); //p1中存储的地址
            printf("----------------------
    ");
            printf("*p = %d
    ", *p); //指针p指向的地址中存储的值
            printf("*p1 = %p
    ", *p1); //p1指向的地址中存储的指针地址
            printf("**p1 = %d
    ", **p1); //p1中存储的指针解引用

     5.2 多级指针介绍

    同理,定义的时候有多少个*,一般也就称为几级的指针

    【写在结尾:】

    『每个人小的时候,都应该会有一个梦想,或者,就称为梦吧』

    ☂业精于勤,荒于嬉;行成于思,毁于随。☂
  • 相关阅读:
    单链表相关笔试题:单链表逆转,约瑟夫环等
    cURL安装和使用笔记
    WSDL中文版——详解
    [c++语法]类
    spark源码解析之基本概念
    apache-spark导入eclipse环境
    zookeeper应用实例
    spring容器加载完毕做一件事情(利用ContextRefreshedEvent事件)
    Let's do our own full blown HTTP server with Netty--转载
    1号店11.11:从应用架构落地点谈高可用高并发高性能--转载
  • 原文地址:https://www.cnblogs.com/wang-biao/p/5661066.html
Copyright © 2011-2022 走看看