数据结构笔记2(c++)_跨函数使用内存的问题
预备知识
1.所有的指针变量只占4个子节 用第一个字节的地址表示整个变量的地址
- //1.cpp 所有的指针变量只占4个子节 用第一个字节的地址表示整个变量的地址
- # include <stdio.h>
- int main(void)
- {
- double * p;
- double x = 66.6;
- p = &x; //x占8个字节 1个字节是8位, 1个字节一个地址, p中只存放一个地址,通常是第一个字节的地址
- // 一个变量占好多好多字节,但是我们通常用一个字节的地址来表示整体的地址,即首地址来表示它
- double arr[] = {1.1, 2.2, 3.3};
- double * q;
- q = &arr[];
- printf("%p\n", q); //%p实际就是以十六进制输出
- //[Out]:
- //0019FF1C
- //0019FF24
- q = &arr[];
- printf("%p\n", q); // 一个指针变量,无论它指向的变量占多少个字节,它本身的大小都是一样的,只占4个字节
- return ;
- }
2.如何通过函数修改实参的值
- //3.cpp 如何通过函数修改实参的值
- # include <stdio.h>
- //void f(int *q);
- void f(int **q);
- int main(void)
- {
- int i = ;
- int * p = &i; //等价于int *p; p = &i;
- printf("%p\n", p);
- f(&p); // 无论参数是什么样的变量,要改变它的值,只需要改变它的地址即可
- printf("%p\n", p);
- return ;
- }
- //void f(int * q)
- //{
- // q = (int *)0xFFFFFFFF;
- //}
- //[Out]:
- //0019FF3C
- //0019FF3C 并未改变主函数的值,因为没有修改它的地址
- void f(int ** q)
- {
- *q = (int *)0xFFFFFFFF;
- }
- //0019FF3C
- //FFFFFFFF
- //以上写法程序不安全,不能写成**的方式来更改main函数中的值
一、结构体
1.为什么会出现结构体
为了表示一些复杂的数据,而普通的基本类型变量无法满足要求(实际上就是java/python中的类)
如: 仅仅用int age; str name 无法区分表示多个学生的整体特征
结构体中没有方法,模拟一个事物模拟得不是很彻底;但好处在于,编写程序时,是以算法为核心的。
学数据结构,算法在c语言/面向过程的语言中,是最好的,但是在java这种面向对象的语言中,算法就不是特别核心了。
2.什么叫结构体
结构体是用户根据实际需要,自己定义的复合数据类型
3.如何使用结构体
- //struct_1.cpp 结构体的使用概述
- # include <stdio.h>
- # include <string.h>
- struct Student //结构体是数据类型,而不是变量,定义变量一定要分配内存,
- //struct Student是一个整体,是一个模型,有三个成员(不是属性,java/python中才叫属性);但是定义结构体并没有分配内存,只是定义了一个新的数据类型
- {
- int sid;
- char name[];
- int age;
- }; //分号不能省
- int main(void)
- {
- struct Student st = {, "zhangsan", }; //用struct Student 这个数据类型,定义了一个变量st,赋值;struct Student一定要整体写
- printf("%d %s %d\n", st.sid, st.name, st.age); // 第一次编写时一定要编译,不然VC6不会提示,编译了一次以后就可以提示了
- //[Out]:
- //1000 zhangsan 20
- // st.sid = 99; //error, c++中不能这样赋值,java/python才可以,必须按照上面的写法
- strcpy(st.name, "lisi"); // strcpy实际上就是复制的作用,在开头要添加上# include <string.h>
- st.age = ;
- printf("%d %s %d\n", st.sid, st.name, st.age);
- //[Out[
- //1000 zhangsan 20
- //1000 lisi 22
- // printf("%d %s %d\n", st); //error
- return ;
- }
- //struct_2.cpp 结构体的两种使用方式
- # include <stdio.h>
- int main(void)
- {
- struct Student st = {, "zhangsan", };
- //st.sid = 99; // 如何使用结构体变量?第一种方式,很少使用,因为不会定义很多变量的名字
- struct Student * pst = &st; // 第二种方式是最常用的: 定义一个指针变量*pst,存放struct Student这种变量地址,
- // struct Student这个变量占多少个字节? int sid --4 ; char name[200] --200 ; int age --4; 共计208个(理论上,实际上很可能不是)
- //pst = &st; //pst存放st的地址,则pst指向st了,*pst就是st了
- pst->sid = ; //pst->sid 等价于(*pst).sid, 没有什么为什么,是规定,只需要记住;而(*pst).sid等价于st.sid, 所以pst->sid 等价于st.sid
- }
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.普通结构体变量和结构体指针变量作为函数传参的问题
- //struct_3.cpp 结构体变量
- # include <stdio.h>
- # include <string.h>
- struct Student
- {
- int sid;
- char name[];
- int age;
- };
- void f(struct Student * pst); //前缀声明
- void g(struct Student st);
- void g2(struct Student * pst);
- int main(void)
- {
- struct Student st;
- f(&st);
- //printf("%d %s %d\n", st.sid, st.name, st.age);
- g(st); // 通过一个函数实现输出,而不是直接输出。将208个字节赋值给另一个函数,这种方式速度慢,浪费空间,不推荐
- g(&st); // 结果输出速度快很多
- return ;
- }
- //[Out]:99 zhangsan 22
- 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); // 通过指针只花了4个字节的空间,也能够实现
- }
- void f(struct Student * pst) // 将st的地址发送给了pst,不是*pst
- {
- (*pst).sid = ; // 普通变量使用.来调用
- strcpy(pst->name, "zhangsan"); // 指针变量用->来调用
- pst->age = ;
- }
二、动态内存的分配和释放
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本身所占用的内存
- # include <stdio.h>
- # include <malloc.h>
- int main(void)
- {
- int a[] = {, , , , };
- int len;
- printf("请输入你需要分配的数组的长度: len = ");
- scanf("%d", &len);
- int *pArr = (int *)malloc(sizeof(int) *len); //分配20个字节,逻辑上的效果:pArr指向了前4个字节,pArr +1 指向后4个
- // int * 强制转换,malloc只能返回第一个字节地址
- //输入4
- //[Out]:4 10 // 不需要写死,可以动态的分配内存malloc(sizeof(int) *len)
- // *pArr = 4; //类似于 a[0]=4;pArr指向前4个字节
- // pArr[1] = 10; //类似于a[1]=10;
- // printf("%d %d", *pArr, pArr[1]);
- // 我们可以把pArr当做一个普通数组来使用
- for (int i=; i<len; ++i) //退出for循环 int就没有了
- scanf("%d", &pArr[i]);
- for (i=; i<len; ++i) //退出for循环 不能再加int, i还能用 要不然就重复定义了
- printf("%d\n", *(pArr+i));
- free(pArr); //程序运行中可以释放,把pArr所代表的动态分配的20个字节的内存释放
- //输入4
- //[Out]:
- // 1 2 3 4
- // 1
- // 2
- // 3
- //
- return ;
- }
2.跨函数使用内存:只能通过动态来实现
- //test1、下程序中,能够通过调用函数fun,使main函数中的指针变量p指向一个合法的整型单元的是: //整型单元-》合法变量
- //A)
- main()
- {
- int *p; //主函数的指针变量p,此时为p中为一个垃圾数字
- fun(p); //通过调用p,使它指向一个合法的整型变量,但是直接调用p,是无法改变数值的 // 参考笔记1-> 如何通过被调函数,修改主调函数中普通变量的值: 1)实参为相关变量的地址:&i
- ...
- }
- int fun(int *p)
- {
- int s;
- p=&s;
- }
- //B)
- main()
- {
- int *p;
- fun(&p); // 参考笔记1-> 如何通过被调函数,修改主调函数中普通变量的值: 1)实参为相关变量的地址:&i
- ...
- }
- int fun(int **q) // 此时*q即为p,将p的地址发送给q,类型一致 参考笔记1-> 如何通过被调函数,修改主调函数中普通变量的值: 2)形参为以该变量的类型为类型的指针变量:*p
- {
- int s;
- *q=&s; // 即为p=&s;参考笔记1-> 如何通过被调函数,修改主调函数中普通变量的值: 1)实参为相关变量的地址:p=&i
- } //执行完此行代码时,p已经保存了一个合法的整型变量地址s了,但问题是执行完成后,该函数就没有了,则s也没有了,最终就是p没有指向合法的变量s了。
- //C)
- #include <stdlib.h>
- main()
- {
- int *p;
- fun(&p); // 添加了取地址符,正确
- ...
- }
- int fun(int **q)
- {
- *q=(int*)malloc(); //构造一个int型数组, 请求系统分配4个字节的内存空间,此时*q即为p,则p指向了4个字节
- } //执行完此行代码时,p已经指向了4个字节,且动态分配的内存没有释放,必须手动通过free释放
- //D)
- #include <stdlib.h>
- main()
- {
- int *p;
- fun(p);// 未添加取地址符,错误
- ...
- }
- int fun(int *p)
- {
- p=(int *)malloc(sizeof(int));
- }
故: 通过此题,可以了解到,我们可以跨函数使用内存
注意:
结构体变量不能加减乘除,但可以相互赋值
普通结构体变量和结构体指针变量作为函数传参的问题
数据结构笔记2(c++)_跨函数使用内存的问题的更多相关文章
- [C语言] 数据结构-预备知识跨函数使用内存
跨函数使用内存 一个函数运行结束,使用malloc函数分配的内存,如果不调用free,就不会释放 在另一个函数中还可以继续使用 #include <stdio.h> #include &l ...
- 数据结构笔记1(c++)_指针
一.数据结构概述 1.定义: 我们如何把现实中大量而复杂的问题,以特定的数据类型和特定的存储结构保存到主存储器(内存)中,以及在此基础上为实现某个功能(比如查找某个元素,删除某个元素,对所有元素进行排 ...
- 使用malloc和free函数进行内存动态分配
一.在学习c语言里面,内存分配这个话题非常有意思,因为我们平时在开发的时候,如果一不小心没注意内存释放的话,写的的程序很容易出错,所以今天就来回顾一下c语言里面的内存动态分配,下面我们先来看一个实例来 ...
- jQuery源代码学习笔记_工具函数_noop/error/now/trim
jQuery源代码学习笔记_工具函数_noop/error/now/trim jquery提供了一系列的工具函数,用于支持其运行,今天主要分析noop/error/now/trim这4个函数: 1.n ...
- 理解Underscore中的_.bind函数
最近一直忙于实习以及毕业设计的事情,所以上周阅读源码之后本周就一直没有进展.今天在写完开题报告之后又抽空看了一眼Underscore源码,发现上次没有看明白的一个函数忽然就豁然开朗了,于是赶紧写下了这 ...
- day16_函数作用域_匿名函数_函数式编程_map_reduce_filter_(部分)内置函数
20180729 补充部分代码 20180727 上传代码 #!/usr/bin/env python # -*- coding:utf-8 -*- # ***************** ...
- Payssion,海外本地支付_海外本地收款_小语种本地支付_外贸收款_外贸网店收款_欧洲本地支付_俄罗斯本地支付_巴西支付_跨境支付_PAYSSION,让跨境支付更轻松!
Payssion,海外本地支付_海外本地收款_小语种本地支付_外贸收款_外贸网店收款_欧洲本地支付_俄罗斯本地支付_巴西支付_跨境支付_PAYSSION,让跨境支付更轻松! 首页 / 关于 ...
- ES6学习笔记<二>arrow functions 箭头函数、template string、destructuring
接着上一篇的说. arrow functions 箭头函数 => 更便捷的函数声明 document.getElementById("click_1").onclick = ...
- python3.4学习笔记(二十) python strip()函数 去空格\n\r\t函数的用法
python3.4学习笔记(二十) python strip()函数 去空格\n\r\t函数的用法 在Python中字符串处理函数里有三个去空格(包括'\n', '\r', '\t', ' ')的函数 ...
随机推荐
- 冰雪奇缘,白色世界:四个IT人的四姑娘山双桥沟游记
去年9月初去了川西的稻城亚丁,体会了金色世界秋日童话,还写了一篇游记<从你的全世界路过-一群程序员的稻城亚丁游记>,也是得到了很多朋友和童鞋的点赞.今年11月初趁着周末的两天时间和朋友去了 ...
- 第421期 Python 周刊
新闻 感谢 Guido 链接: https://blog.dropbox.com/topics/company/thank-you--guido Python之父 Guido van Rossum 即 ...
- Microsemi Libero系列教程(二)——新建点灯工程
前言 上一篇文章,介绍了Microsemi Libero系列教程(一)-Libero开发环境介绍,下载,安装与注册,作为嵌入式开发中的Hello World,点灯是再也基础不过的实验了,通过点灯实验, ...
- 用Python6种方法:给定一个不超过5位的正整数,判断有几位
方法一:作比较 a=int(input(">>>>")) if a<10: print(1) elif a<100: #第一个条件已经过滤了大于 ...
- ES6之Class类
一.Class的基本语法 1.简介 基本上,ES6的class可以看作只是一个 语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让 对象原型 的写法更加清晰.更像面向对象编程的语法而 ...
- 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 ...
- iOS中nil、 Nil、 NULL和NSNull的区别
参考链接:https://www.jianshu.com/p/c3017ae6684a
- 8 个 Tips 让你更好的进行 Code Review
摘要: Code Review 可以提高代码质量. 原文:Elevenbeans 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 原文地址:https://kellysutton.co ...
- Linux(Centos7)下Mysql的安装
1.1 查看mysql的安装路径: [root@bogon ~]# whereis mysql mysql: /usr/bin/mysql /usr/lib/mysql /usr/share/mysq ...
- diango中的MTV——FBV/CBV以及装饰器的复用问题解决
MVC M: model 模型 与数据库交互 V: view 视图 HTML C:controller 控制器 流程 和 业务逻辑 MTV M:model ORM T:template 模板 HTML ...