风也温柔

计算机科学知识库

c语言数据结构和指针 6_ 所有的指针变量只占4个子节用第一个字节的地址表示整个变量的地址

  (郝斌老师上课内容)

  简介:预备知识:

  4_ 预备知识_指针1

  5 预备知识_指针2

  6 所有的指针变量只占4个子节用第一个字节的地址表示整个变量的地址

  7_ 如何通过函数修改实参的值

  8_ 结构体的使用概述

  9_ ()动态分配内存概述

  10_ 跨函数使用内存讲解及其示例

  4_ 预备知识_指针_1 5_ 预备知识_指针_2

  指针的重要性:指针是C语言的灵魂

  定义:地址:地址是内存单元的编号,从0开始的非负整数,范围:0-【0-4G-1】

  指针:指针就是地址,地址就是指针。

  指针变量是存放内存单元地址的变量。

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

  分类:

  1、基本类型指针:

  基本概念

  int i=10;

  int p = &i; //等价于 int p; p = &i;

  详解这两部操作:

  1)p存放了i的地址,所以我们说p指向了i

  2)p和i是完全不同的两个变量,修改其中的任意一个变量的值,不会影响另一变量的值

  3)p指向i, p 就是 i 变量本身。更形象的说所有出现 p 的地方都可以换成i,所有出现i

  的地方都可以换成 *p

   # include

    int main(void)
    {
        int * p; //p是个变量名字, int * 表示该p变量只能存储int类型变量的地址
        int i = 10;
        int j;
        p = &i;//p保存i的地址,p指向i;修改p的值不影响i的值,修改i的值不影响p的值;任何场合下,*p和i可以互换。*p等价于i。
        j = *p; // 等价于 j = i;
        printf("i = %d, j = %d, *p = %d\n", i, j, *p);
        //p = 10;  //error
        return 0;
    }

  在这里插入图片描述

  总结:

  a、如何一个指针变量(假定为p)存放了某个普通变量(假定为i)的地址,那我们就可以说:“p指向了i”, 但p与i是两个不同的变量,修改p的值不影响i的值,修改i的值不影响p的值.

  b、 p 等价于 i 或者说 p 可以与 i 在任何地方互换

  c、如果一个指针变量指向了某个普通变量c语言数据结构和指针 6_ 所有的指针变量只占4个子节用第一个字节的地址表示整个变量的地址,则 指针变量 就完全等价于 该普通变量

  注意:

  指针变量也是变量,只不过它存放的不能是内存单元的内容,只能存放内存单元的地址普通变量前不能加常量和表达式前不能加&

  如何通过被调函数修改主调函数中普通变量的值:

  Ⅰ 实参为相关变量的地址

  Ⅱ 形参为以该变量的类型为类型的指针变量

  Ⅲ 在被调函数中通过 *形参变量名 的方式就可以修改主函数相关变量的值

   //这样并不能改变i的值,因为只能有实参将数传递给形参,却不能使得形参将值传给实参

    # include 
    void f(int i) 
    {
        i = 100; // 
    }
    int main(void)
    {
        int i = 9;
        f(&i);
        printf("i = %d\n", i);
        return 0;
    }
    //结果为9,没能改变主调函数中i的值

   # include

    void f(int * p) //不是定义了一个名字叫做*p的形参, 而是定义了一个形参,该形参名字叫做p,它的类型是int *
    {
        *p = 100; //
    }
    int main(void)
    {
    <p>![c语言数据结构和指针_c语言指向结构体的指针_c语言中的函数指针][2]

        int i = 9;
        f(&i);  //将i的地址传给了p,那么就说p指向i,也就是*p和 i完全等价了,也就是*p可以与i在任何地方互换
        printf("i = %d\n", i);
        return 0;
    }

</p>

  2.指针和数组的关系

  变量并不一定连续分配,随机分配内存。

  指针 和 一维数组

  数组名:一维数组名是个指针常量c语言数据结构和指针

  它存放的是一维数组第一个元素的地址,

  它的值不能被改变

  一维数组名指向的是数组的第一个元素

  下标和指针的关系:

  *a[i] (a+i)

  假设指针变量的名字为p

  则p+i的值是 p+i * (p所指向的变量所占的字节数)

  指针变量的运算

  指针变量不能相加,不能相乘,不能相除

  如果两指针变量属于同一数组,则可以相减

  指针变量可以加减一整数c语言数据结构和指针,前提是最终结果不能超过指针允许指向的范围

  p+i的值是p+i*(p所指向的变量所占的字节数)

  p-i的值是p-i*(p所指向的变量所占的字节数)

  p++ 等价 p+1

  p-- 等价 p-1

   # include

    int main(void)
    {
        int a[5] = {1,2,3,4,5}; //a指向的就是a[0]
        //a[3] == *(3+a);  //指向 第四个元素
        printf("%p\n", a+1);//%p表示输出地址
        printf("%p\n", a+2);
        printf("%p\n", a+3);   //输出的地址是连续的
        printf("%d\n", *a+3); //*a+3等价于 a[0]+3
        return 0;
    }

  在这里插入图片描述

  如何通过被调函数修改主调函数中一维数组的内容【如何界定一维数组】

  两个参数:

  存放数组首元素的指针变量

  存放数组元素长度的整型变量

   # include

    void Show_Array(int * p, int len)
    {
        int i = 0;
        for (i=0; isid

    &emsp;&emsp;pst所指向的结构体变量中的sid这个成员

    &emsp;&emsp;<pre><code># include 
    # include 
    struct Student
    {    
        int sid;
        char name[200];
        int age;
    }; //分号不能省
    int main(void)
    {
        struct Student st = {1000, "zhangsan", 20};
        //st.sid = 99;  //第一种方式
        struct Student * pst; //定义了一个指针变量pst,存的是struct Student 的地址,
        pst = &st;
        pst->sid = 99;  //第二种方式  pst->sid 等价于 (*pst).sid  而(*pst).sid等价于 st.sid,  所以pst->sid 等价于 st.sid
        return 0;
    }

   # include

    # include 
    struct Student
    {    
    <p>![c语言数据结构和指针_c语言指向结构体的指针_c语言中的函数指针][4]

        int sid;
        char name[200];
        int age;
    }; //分号不能省
    void f(struct Student * pst);
    void g(struct Student st);
    void g2(struct Student *pst);
    int main(void)
    {
        struct Student st;  //已经为st分配好了内存,但是是一个垃圾值     //int  i  //为i分配了内存
        f(&st);
        g2(&st);
        //printf("%d %s %d\n", st.sid, st.name, st.age);//输出方法一
        return 0;
    }
    //这种方式耗内存 耗时间 不推荐
    //整体变量赋值//输出方法二,速度慢,耗空间,耗内存,不推荐
    void g(struct Student st)
    {
        printf("%d %s %d\n", st.sid, st.name, st.age);    
    }
    void g2(struct Student *pst)
    {
        printf("%d %s %d\n", pst->sid, pst->name, pst->age);    
    }
    void f(struct Student * pst)
    {
        (*pst).sid = 99;
        strcpy(pst->name, "zhangsan");
        pst->age = 22;
    }

</p>

  9_ ()动态分配内存概述

  没有使用()函数的都是静态的,使用这个函数的都是动态的

  动态内存分配和释放:

  动态构造一维数组

  假设动态构造一个int型数组

   int p = (int) malloc(int len);

  1、只有一个int型的形参,表示要求系统分配的字节数

  2、函数的功能是请求系统len个字节的内存空间,如果请求分配成功,则返回第一个字节的地址,如果分配不成功,则返回NULL

  3、函数能且只能返回第一个字节的地址,所以我们需要把这个无任何实际意义的第一个字节的地址(俗称干地址)转化为一个有实际意义的地址,因此前面必须加(数据类型 *),表示把这个无实际意义的第一个字节的地址转化为相应类型的地址。如:

  int p = (int )(50);

  表示将系统分配好的50个字节的第一个字节的地址转化为int *型的地址,更准确的说是把第一个字节的地址转化为四个字节的地址,这样 p就指向了第一个的四个字节,p+1就指向了第2个的四个字节, p+i就指向了第i+1个的4个字节。p[0]就是第一个元素, p[i]就是第i+1个元素

   p = ( )(80);表示将系统分配好的80个字节的第一个字节的地址转化为 *型的地址,更准确的说是把第一个字节的地址转化为8个字节的地址,这样p就指向了第一个的8个字节,p+1就指向了第2个的8个字节, p+i就指向了第i+1个的8个字节。p[0]就是第一个元素, p[i]就是第i+1个元素

  free(p)

  释放p所指向的内存,而不是释放p本身所占用的内存动态内存的分配和释放

   # include

    # include 
    int main(void)
    {
        int a[5] = {4, 10, 2, 8, 6};//静态分配,直到程序结束才会释放,但是如果动态,就可以随时自己进行释放
        
        int len;
        printf("请输入你需要分配的数组的长度: len = ");
        scanf("%d", &len);
            //int * pArr = (int *)malloc(sizeof(int) * len);逻辑上和int a[5] = {4, 10, 2, 8, 6};等价
        int * pArr = (int *)malloc(sizeof(int) * len);//sizeof(int)整型占的字节数
            //一共5个元素,sizeof(int) * len其实也就是20个字节,请求操作系统给20个字节的空间。
            //malloc函数只返回第一个字节函数,int类型只返回第一个地址,double类型也只返回第一个字节,所以没有实际意义,需要(int *)强制转换,来告诉编译器返回的是整形的地址,所以就知道pArr指向的是前面四个的地址
     //    *pArr = 4;  //类似于 a[0] = 4;
     //    pArr[1] = 10; //类似于a[1] = 10;
     //    printf("%d %d\n", *pArr, pArr[1]);
        //我们可以把pArr当做一个普通数组来使用
        for (int i=0; iage);
    }
    struct Student * CreateStudent(void)
    {
        struct Student * p = (struct Student *)malloc(sizeof(struct Student));
        p->sid = 99;
        p->age = 88;
        return p;
    }

  在这里插入图片描述

  文章来源:https://blog.csdn.net/weixin_43384257/article/details/89068960