预备知识

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

  1. //1.cpp 所有的指针变量只占4个子节 用第一个字节的地址表示整个变量的地址
  2. # include <stdio.h>
  3.  
  4. int main(void)
  5. {
  6. double * p;
  7. double x = 66.6;
  8.  
  9. p = &x; //x占8个字节 1个字节是8位, 1个字节一个地址, p中只存放一个地址,通常是第一个字节的地址
  10. // 一个变量占好多好多字节,但是我们通常用一个字节的地址来表示整体的地址,即首地址来表示它
  11.  
  12. double arr[] = {1.1, 2.2, 3.3};
  13. double * q;
  14. q = &arr[];
  15. printf("%p\n", q); //%p实际就是以十六进制输出
  16. //[Out]:
  17. //0019FF1C
  18. //0019FF24
  19. q = &arr[];
  20. printf("%p\n", q); // 一个指针变量,无论它指向的变量占多少个字节,它本身的大小都是一样的,只占4个字节
  21.  
  22. return ;
  23. }

2.如何通过函数修改实参的值

  1. //3.cpp 如何通过函数修改实参的值
  2. # include <stdio.h>
  3.  
  4. //void f(int *q);
  5. void f(int **q);
  6.  
  7. int main(void)
  8. {
  9. int i = ;
  10. int * p = &i; //等价于int *p; p = &i;
  11.  
  12. printf("%p\n", p);
  13. f(&p); // 无论参数是什么样的变量,要改变它的值,只需要改变它的地址即可
  14. printf("%p\n", p);
  15.  
  16. return ;
  17.  
  18. }
  19.  
  20. //void f(int * q)
  21. //{
  22. // q = (int *)0xFFFFFFFF;
  23. //}
  24. //[Out]:
  25. //0019FF3C
  26. //0019FF3C 并未改变主函数的值,因为没有修改它的地址
  27.  
  28. void f(int ** q)
  29. {
  30. *q = (int *)0xFFFFFFFF;
  31. }
  32. //0019FF3C
  33. //FFFFFFFF
  34. //以上写法程序不安全,不能写成**的方式来更改main函数中的值

一、结构体

  1.为什么会出现结构体

    为了表示一些复杂的数据,而普通的基本类型变量无法满足要求(实际上就是java/python中的类)

    如: 仅仅用int age; str name 无法区分表示多个学生的整体特征

    结构体中没有方法,模拟一个事物模拟得不是很彻底;但好处在于,编写程序时,是以算法为核心的。

    学数据结构,算法在c语言/面向过程的语言中,是最好的,但是在java这种面向对象的语言中,算法就不是特别核心了。

  2.什么叫结构体

    结构体是用户根据实际需要,自己定义的复合数据类型

  3.如何使用结构体

  1. //struct_1.cpp 结构体的使用概述
  2. # include <stdio.h>
  3. # include <string.h>
  4.  
  5. struct Student //结构体是数据类型,而不是变量,定义变量一定要分配内存,
  6. //struct Student是一个整体,是一个模型,有三个成员(不是属性,java/python中才叫属性);但是定义结构体并没有分配内存,只是定义了一个新的数据类型
  7. {
  8. int sid;
  9. char name[];
  10. int age;
  11.  
  12. }; //分号不能省
  13.  
  14. int main(void)
  15. {
  16. struct Student st = {, "zhangsan", }; //用struct Student 这个数据类型,定义了一个变量st,赋值;struct Student一定要整体写
  17. printf("%d %s %d\n", st.sid, st.name, st.age); // 第一次编写时一定要编译,不然VC6不会提示,编译了一次以后就可以提示了
  18. //[Out]:
  19. //1000 zhangsan 20
  20. // st.sid = 99; //error, c++中不能这样赋值,java/python才可以,必须按照上面的写法
  21. strcpy(st.name, "lisi"); // strcpy实际上就是复制的作用,在开头要添加上# include <string.h>
  22. st.age = ;
  23. printf("%d %s %d\n", st.sid, st.name, st.age);
  24. //[Out[
  25. //1000 zhangsan 20
  26. //1000 lisi 22
  27. // printf("%d %s %d\n", st); //error
  28. return ;
  29. }
  1. //struct_2.cpp 结构体的两种使用方式
  2. # include <stdio.h>
  3.  
  4. int main(void)
  5. {
  6. struct Student st = {, "zhangsan", };
  7. //st.sid = 99; // 如何使用结构体变量?第一种方式,很少使用,因为不会定义很多变量的名字
  8. struct Student * pst = &st; // 第二种方式是最常用的: 定义一个指针变量*pst,存放struct Student这种变量地址,
  9. // struct Student这个变量占多少个字节? int sid --4 ; char name[200] --200 ; int age --4; 共计208个(理论上,实际上很可能不是)
  10. //pst = &st; //pst存放st的地址,则pst指向st了,*pst就是st了
  11. pst->sid = ; //pst->sid 等价于(*pst).sid, 没有什么为什么,是规定,只需要记住;而(*pst).sid等价于st.sid, 所以pst->sid 等价于st.sid
  12.  
  13. }

    3.1.两种方式:

      struct Student st = {1000, "zhangsan", 20};

      struct Student * pst = &st;

      3.1.1. st.sid

      3.1.2.pst->sid  

        //pst所指向的结构体变量中,有sid这个成员(最终表示的就是sid这个成员)

  4.注意事项:

    4.1.结构体变量不能加减乘除,但可以相互赋值;

    4.2.普通结构体变量和结构体指针变量作为函数传参的问题

  1. //struct_3.cpp 结构体变量
  2. # include <stdio.h>
  3. # include <string.h>
  4.  
  5. struct Student
  6. {
  7. int sid;
  8. char name[];
  9. int age;
  10.  
  11. };
  12.  
  13. void f(struct Student * pst); //前缀声明
  14. void g(struct Student st);
  15. void g2(struct Student * pst);
  16.  
  17. int main(void)
  18. {
  19. struct Student st;
  20.  
  21. f(&st);
  22. //printf("%d %s %d\n", st.sid, st.name, st.age);
  23. g(st); // 通过一个函数实现输出,而不是直接输出。将208个字节赋值给另一个函数,这种方式速度慢,浪费空间,不推荐
  24. g(&st); // 结果输出速度快很多
  25.  
  26. return ;
  27. }
  28. //[Out]:99 zhangsan 22
  29.  
  30. void g(struct Student st)
  31. {
  32. printf("%d %s %d\n", st.sid, st.name, st.age);
  33. }
  34.  
  35. void g2(struct Student *pst)
  36. {
  37. printf("%d %s %d\n", pst->sid, pst->name, pst->age); // 通过指针只花了4个字节的空间,也能够实现
  38.  
  39. }
  40.  
  41. void f(struct Student * pst) // 将st的地址发送给了pst,不是*pst
  42. {
  43. (*pst).sid = ; // 普通变量使用.来调用
  44. strcpy(pst->name, "zhangsan"); // 指针变量用->来调用
  45. pst->age = ;
  46. }

二、动态内存的分配和释放

  1.动态构造一维数组

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

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

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

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

      1.3.malloc函数能且只能返回第一个字节的地址,所以我们需要把这个无实际意义的第一个字节的地址(俗称干地址)转化为一个有实际意义的地址

        因此,malloc前面必须加(数据类型*),表示把这个无实际意义的第一个地址,通过强制类型转换,来告诉编译器,我们返回的第一个类型的地址,它到底是整型地址,还是其他类型的地址,即为前面(int *)类型的含义。

        如: int *p = (int *)malloc(50):

            表示将系统分配好的50个字节的第一个字节的地址转化为整型地址,更准确的说是把第一个字节的地址转化为4个字节,这样p就指向了第一个的4个字节,

            p+1就指向了第2个的4个字节。p[0]就是第一个元素

          double *p = (double *)malloc(80);

            表示将系统分配好的80个字节的第一个字节的地址转化浮点型地址,更准确的说是把第一个字节的地址转化为8个字节,这样p就指向了第一个的8个字节,

            p+1就指向了第2个的8个字节。p+i就指向了第i+1个的8个字节。p[0]就是第一个元素

     free(p) 释放p所指向的内存,而不是释放p本身所占用的内存

  1. # include <stdio.h>
  2. # include <malloc.h>
  3.  
  4. int main(void)
  5. {
  6. int a[] = {, , , , };
  7. int len;
  8. printf("请输入你需要分配的数组的长度: len = ");
  9. scanf("%d", &len);
  10. int *pArr = (int *)malloc(sizeof(int) *len); //分配20个字节,逻辑上的效果:pArr指向了前4个字节,pArr +1 指向后4个
  11. // int * 强制转换,malloc只能返回第一个字节地址
  12. //输入4
  13. //[Out]:4 10 // 不需要写死,可以动态的分配内存malloc(sizeof(int) *len)
  14.  
  15. // *pArr = 4; //类似于 a[0]=4;pArr指向前4个字节
  16. // pArr[1] = 10; //类似于a[1]=10;
  17. // printf("%d %d", *pArr, pArr[1]);
  18.  
  19. // 我们可以把pArr当做一个普通数组来使用
  20. for (int i=; i<len; ++i) //退出for循环 int就没有了
  21. scanf("%d", &pArr[i]);
  22.  
  23. for (i=; i<len; ++i) //退出for循环 不能再加int, i还能用 要不然就重复定义了
  24. printf("%d\n", *(pArr+i));
  25.  
  26. free(pArr); //程序运行中可以释放,把pArr所代表的动态分配的20个字节的内存释放
  27. //输入4
  28. //[Out]:
  29. // 1 2 3 4
  30. // 1
  31. // 2
  32. // 3
  33. //
  34.  
  35. return ;
  36. }

  2.跨函数使用内存:只能通过动态来实现

  1. //test1、下程序中,能够通过调用函数fun,使main函数中的指针变量p指向一个合法的整型单元的是: //整型单元-》合法变量
  2. //A)
  3. main()
  4. {
  5. int *p; //主函数的指针变量p,此时为p中为一个垃圾数字
  6. fun(p); //通过调用p,使它指向一个合法的整型变量,但是直接调用p,是无法改变数值的 // 参考笔记1-> 如何通过被调函数,修改主调函数中普通变量的值: 1)实参为相关变量的地址:&i
  7. ...
  8. }
  9. int fun(int *p)
  10. {
  11. int s;
  12. p=&s;
  13. }
  14.  
  15. //B)
  16. main()
  17. {
  18. int *p;
  19. fun(&p); // 参考笔记1-> 如何通过被调函数,修改主调函数中普通变量的值: 1)实参为相关变量的地址:&i
  20. ...
  21. }
  22. int fun(int **q) // 此时*q即为p,将p的地址发送给q,类型一致 参考笔记1-> 如何通过被调函数,修改主调函数中普通变量的值: 2)形参为以该变量的类型为类型的指针变量:*p
  23. {
  24. int s;
  25. *q=&s; // 即为p=&s;参考笔记1-> 如何通过被调函数,修改主调函数中普通变量的值: 1)实参为相关变量的地址:p=&i
  26.  
  27. } //执行完此行代码时,p已经保存了一个合法的整型变量地址s了,但问题是执行完成后,该函数就没有了,则s也没有了,最终就是p没有指向合法的变量s了。
  28.  
  29. //C)
  30. #include <stdlib.h>
  31. main()
  32. {
  33. int *p;
  34. fun(&p); // 添加了取地址符,正确
  35. ...
  36. }
  37. int fun(int **q)
  38. {
  39. *q=(int*)malloc(); //构造一个int型数组, 请求系统分配4个字节的内存空间,此时*q即为p,则p指向了4个字节
  40.  
  41. } //执行完此行代码时,p已经指向了4个字节,且动态分配的内存没有释放,必须手动通过free释放
  42.  
  43. //D)
  44. #include <stdlib.h>
  45. main()
  46. {
  47. int *p;
  48. fun(p);// 未添加取地址符,错误
  49. ...
  50. }
  51.  
  52. int fun(int *p)
  53. {
  54. p=(int *)malloc(sizeof(int));
  55. }

    故: 通过此题,可以了解到,我们可以跨函数使用内存

注意:

  结构体变量不能加减乘除,但可以相互赋值

  普通结构体变量和结构体指针变量作为函数传参的问题

数据结构笔记2(c++)_跨函数使用内存的问题的更多相关文章

  1. [C语言] 数据结构-预备知识跨函数使用内存

    跨函数使用内存 一个函数运行结束,使用malloc函数分配的内存,如果不调用free,就不会释放 在另一个函数中还可以继续使用 #include <stdio.h> #include &l ...

  2. 数据结构笔记1(c++)_指针

    一.数据结构概述 1.定义: 我们如何把现实中大量而复杂的问题,以特定的数据类型和特定的存储结构保存到主存储器(内存)中,以及在此基础上为实现某个功能(比如查找某个元素,删除某个元素,对所有元素进行排 ...

  3. 使用malloc和free函数进行内存动态分配

    一.在学习c语言里面,内存分配这个话题非常有意思,因为我们平时在开发的时候,如果一不小心没注意内存释放的话,写的的程序很容易出错,所以今天就来回顾一下c语言里面的内存动态分配,下面我们先来看一个实例来 ...

  4. jQuery源代码学习笔记_工具函数_noop/error/now/trim

    jQuery源代码学习笔记_工具函数_noop/error/now/trim jquery提供了一系列的工具函数,用于支持其运行,今天主要分析noop/error/now/trim这4个函数: 1.n ...

  5. 理解Underscore中的_.bind函数

    最近一直忙于实习以及毕业设计的事情,所以上周阅读源码之后本周就一直没有进展.今天在写完开题报告之后又抽空看了一眼Underscore源码,发现上次没有看明白的一个函数忽然就豁然开朗了,于是赶紧写下了这 ...

  6. day16_函数作用域_匿名函数_函数式编程_map_reduce_filter_(部分)内置函数

    20180729    补充部分代码 20180727    上传代码 #!/usr/bin/env python # -*- coding:utf-8 -*- # ***************** ...

  7. Payssion,海外本地支付_海外本地收款_小语种本地支付_外贸收款_外贸网店收款_欧洲本地支付_俄罗斯本地支付_巴西支付_跨境支付_PAYSSION,让跨境支付更轻松!

    Payssion,海外本地支付_海外本地收款_小语种本地支付_外贸收款_外贸网店收款_欧洲本地支付_俄罗斯本地支付_巴西支付_跨境支付_PAYSSION,让跨境支付更轻松!       首页 / 关于 ...

  8. ES6学习笔记<二>arrow functions 箭头函数、template string、destructuring

    接着上一篇的说. arrow functions 箭头函数 => 更便捷的函数声明 document.getElementById("click_1").onclick = ...

  9. python3.4学习笔记(二十) python strip()函数 去空格\n\r\t函数的用法

    python3.4学习笔记(二十) python strip()函数 去空格\n\r\t函数的用法 在Python中字符串处理函数里有三个去空格(包括'\n', '\r', '\t', ' ')的函数 ...

随机推荐

  1. 冰雪奇缘,白色世界:四个IT人的四姑娘山双桥沟游记

    去年9月初去了川西的稻城亚丁,体会了金色世界秋日童话,还写了一篇游记<从你的全世界路过-一群程序员的稻城亚丁游记>,也是得到了很多朋友和童鞋的点赞.今年11月初趁着周末的两天时间和朋友去了 ...

  2. 第421期 Python 周刊

    新闻 感谢 Guido 链接: https://blog.dropbox.com/topics/company/thank-you--guido Python之父 Guido van Rossum 即 ...

  3. Microsemi Libero系列教程(二)——新建点灯工程

    前言 上一篇文章,介绍了Microsemi Libero系列教程(一)-Libero开发环境介绍,下载,安装与注册,作为嵌入式开发中的Hello World,点灯是再也基础不过的实验了,通过点灯实验, ...

  4. 用Python6种方法:给定一个不超过5位的正整数,判断有几位

    方法一:作比较 a=int(input(">>>>")) if a<10: print(1) elif a<100: #第一个条件已经过滤了大于 ...

  5. ES6之Class类

    一.Class的基本语法 1.简介 基本上,ES6的class可以看作只是一个 语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让 对象原型 的写法更加清晰.更像面向对象编程的语法而 ...

  6. FCC---CSS Flexbox: Apply the flex-direction Property to Create Rows in the Tweet Embed

    The header and footer in the tweet embed example have child items that could be arranged as rows usi ...

  7. iOS中nil、 Nil、 NULL和NSNull的区别

    参考链接:https://www.jianshu.com/p/c3017ae6684a

  8. 8 个 Tips 让你更好的进行 Code Review

    摘要: Code Review 可以提高代码质量. 原文:Elevenbeans 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 原文地址:https://kellysutton.co ...

  9. Linux(Centos7)下Mysql的安装

    1.1 查看mysql的安装路径: [root@bogon ~]# whereis mysql mysql: /usr/bin/mysql /usr/lib/mysql /usr/share/mysq ...

  10. diango中的MTV——FBV/CBV以及装饰器的复用问题解决

    MVC M: model 模型 与数据库交互 V: view 视图 HTML C:controller 控制器 流程 和 业务逻辑 MTV M:model ORM T:template 模板 HTML ...