C/C++心得-从内存开始
因工作与自身各方面需要,开始重新学C,其实说重新也不太准,原来只是大学里面接触过,且还未得多少精髓就转其他开发,不过也正是因此才有了重新学习的必要,基础部分的心得将通过博文记录下来,对于初学者应该有些用处;当然这里只是心得,并不能直接用来学习C,但应该可以少走一些弯路。
看了不少资料,最终我个人的认识是:C/C++的难点在C,C的精华是指针,想学好指针就要清楚的认识计算机内存。
1.了解内存必要知识
介绍内存就先从存储单位开始;写这篇文章的时间是在2014年冬季,当时日常生活中我们接触到的存储设备是电脑硬盘、手机内存卡;最常接触的存储单位是MB(读作“兆”)和GB(读作“吉”)。而实际上,计算机语言中最小的存储单位是bit(读作“比特”),计算机中许多数据的表示都是以二进制做单位的,而1比特就代表一个二进制数位,只能存储0或者1;Byte是计算机中最常用的单位,8bit为1byte,许多地方以1b代表1bit,1B代表1byte。1 Kilobyte等于1000byte(计算机中为二进制计算1kilobyte等于2的十次方byte,也就是1024byte)。下面介绍一些其他单位的换算:
1 kilobyte kB = 1000 (10^3) byte
1 megabyte MB = 1 000 000 (10^6) byte
1 gigabyte GB = 1 000 000 000 (10^9) byte
1 terabyte TB = 1 000 000 000 000 (10^12) byte
1 petabyte PB = 1 000 000 000 000 000 (10^15) byte
1 exabyte EB = 1 000 000 000 000 000 000 (10^18) byte
1 zettabyte ZB = 1 000 000 000 000 000 000 000 (10^21) byte
1 yottabyte YB = 1 000 000 000 000 000 000 000 000 (10^24) byte
1 brontobyte BB = 1 000 000 000 000 000 000 000 000 000 (10^27)byte
1 nonabyte NB = 1 000 000 000 000 000 000 000 000 000 000 (10^30) byte
上面也是存储介质(如:硬盘)厂商使用的单位,而在计算机系统中识别是使用二进制的千来换算,所以识别的时候容量总会小一些,二进制换算如下:
1 kilobyte kB = 1024 (2^10) byte
1 megabyte MB = 1 048 576 (2^20) byte
1 gigabyte GB = 1 073 741 824 (2^30) byte
1 terabyte TB = 1 099 511 627 776 (2^40) byte
1 petabyte PB = 1 125 899 906 842 624 (2^50) byte
1 exabyte EB = 1 152 921 504 606 846 976 (2^60) byte
1 zettabyte ZB = 1 180 591 620 717 411 303 424(2^70) byte
1 yottabyte YB = 1 208 925 819 614 629 174 706 176 (2^80) byte
1 brontobyte BB = 1 237 940 039 285 380 274 899 124 224(2^90)byte
1 nonabyte NB = 1 267 650 600 228 229 401 496 703 205 376(2^100) byte
当然,其实TB后面的单位基本都用不到,这些东西做个了解,记住计算机系统中除了开始的bit和byte,后面的单位都是前面的1024倍即可;
32位系统中的32位某些意义上指的是CPU一次能处理的最大位数,每一位的单位为byte,以二进制计算,所以32位系统CPU一次能处理的值为2的32次方,最后得出4GB,所以32位系统支持的理论最大内存为4GB,当然实际情况下可能会小一些。
2.C语言中的数据类型
数据类型本质是不同大小内存块的别名,且不同的数据类型对应着不同的解析方式,这是我对数据类型的认识,下面举例介绍。
1 #include<stdio.h> 2 #include<stdlib.h> 3 4 void main() 5 { 6 printf("%d\n",sizeof(int)); 7 printf("%d\n",sizeof(char)); 8 system("pause") ; 9 }
上面是一个较完整的命令行窗口的代码块,稍微学过C的应该可以看懂上面的代码,printf函数可以在命令行中输出字符串,sizeof函数可以计算类型或者变量在内存中占用的大小(以byte为单位)。 变量是在通过数据类型定义后即占用了该类型对应的内存大小。32位程序下上面代码的运行结果应该如下(其他位数系统可能不同,这里仅以32位系统举例):
4 1 请按任意键继续. . .
这说明int类型在内存中占有4byte,char占有1byte。先来说说数字本身,即4和1究竟有什么意义?其实这个数字表明该类型所能表示多少种不同的值,比如int占4字节,也就是32bit,而前面说了,1bit可以表示0和1两种值,那么int可以表示2的32次方种不同的值,即4294967296种不同的值。当然这并不是说int类型最大可以设置为4294967296,因为int类型本身还有负数,实际来说int类型值的范围负数非负数各占一半,即表示范围为:-2147483648至+2147483647;有时候并用不到负数,所有可以在int前面加上unsigned,即unsigned int,这种类型值的范围为:0至+4294967295。可以利用下面代码做测试,在这类数上再加1,就会溢出,会使变量变成其类型的最小值。
1 #include<stdio.h> 2 #include<stdlib.h> 3 4 void main() 5 { 6 unsigned int ui = 4294967295; 7 int i = 2147483647; 8 printf("i=%d\n", i); 9 printf("ui=%u\n", ui); 10 system("pause"); 11 }
根据以上可以推断,char占用的1byte内存空间可以表示的值范围为-128至+127;同样,unsigned char 可表示的值范围为0-255。那么int和char除了占用的内存空间不同还有什么区别?先看下面的测试代码:
1 #include<stdio.h> 2 #include<stdlib.h> 3 4 void main() 5 { 6 int i = 90; 7 int j = 'c'; 8 char a = 100; 9 char b = 'b'; 10 printf("作为整数输出:"); 11 printf("i=%d,j=%d,a=%d,b=%d\n", i, j, a, b); 12 printf("作为字符输出:"); 13 printf("i=%c,j=%c,a=%c,b=%c\n", i, j, a, b); 14 system("pause"); 15 }
首先,上面这段代码应该是编译通过的,看过C数据类型的都知道,int是存储整数的数据类型,char是存储字符的数据类型,但是上面代码中可以为int类型变量j赋值字符,也可以为char类型变量a赋值整数,且上面4个变量都可以用%d作为整数输出,也都可以用%c作为字符输出。这就是所谓的解析方式了,数据类型除了其分配的内存大小不同,解析方式也不同,而int和char类型的解析方式可以相互间通用。我们都知道计算机最后的数据都会转换为0和1的二进制数据,0到9之间的数字也都是通过二进制转换而来的,那么字符其实也是通过数字转化而来的,二进制到十进制我们可以直接计算出来,但数字到字符的转化,是人为规定的,这个规定的表就是ASCII表。ASCII表的具体对应关系这里就不贴了,各类C资料上应该都有。上面代码的执行结果如下:
作为整数输出:i=90,j=99,a=100,b=98
作为字符输出:i=Z,j=c,a=d,b=b
请按任意键继续. . .
int和char类型可以相互转化是因为ASCII表对应的映射关系,且我们上面代码中的测试数据没有越界(没有超过char类型值的范围),从上面的执行结果来看,我们知道ASCII中整数90对应大写字母Z,整数99对应小写字母c,整数100对应小写字母d,整数98对应小写字母b。因为用于测试,所以我用了char和int类型,如果使用float或者double类型可能就得不出这样反应其映射性关系的结果,因为它们的解析完全不同。
3.内存四区
本文主要将内存,之前都是一些零碎的知识,现在具体来说说C语言中的内存;C语言中用户可以操作的内存基本都是定义变量或者通过malloc或者relloc这类函数分配到的,如果要获取变量的地址,可以通过&符号。
C语言程序的内存,传统意义来说分为四个区,分别是 代码区、数据区(全局区、常量区)、栈区(临时区)、堆区。
代码区存放编译器编译C语言程序后的二进制代码,C语言中无法获取其地址,所以不过多说明。
数据区,是存储程序全局变量的地方,其中全局变量放在数据区的全局区域,常量放在全局区的常量区域,常量不能获取地址,全局变量可以。该区域内存会在程序结束后释放。
栈区(临时区),程序直接定义的变量都放在栈区,在变量所在函数执行完成的时候,函数内部定义的栈区变量会被释放(释放的意义是不能再正确取得该变量的值)。每个程序所能用的栈区内存很有限,一般只有1M。因为其在函数调用完成后即被释放的特性,所以也被称为临时区。
堆区,通过malloc或者relloc这类函数分配的内都存放在堆区,一般使用堆区的原因是需要用到大块的内存,或者是希望变量在其所在函数调用结束后变量内存仍能使用,那么就会使用到堆区,堆区的缺点是内存需要手动使用free函数释放,容易造成内存泄漏(指的是变量使用完了仍然占着内存空间,如果占用过大会影响系统或其他程序运行)。C语言中用的最多的内存区即堆区和栈区。下面代码是内存四区变量的示例:
1 #include<stdio.h> 2 #include<stdlib.h> 3 4 int x = 0; // 数据区 全局区变量 5 int y;// 数据区 全局区变量 x,y都会在程序结束时释放内存 6 7 8 int * ExampleStack() 9 { 10 int i = 0; 11 int j; // i和j是栈区变量, 虽然i比j先定义但因栈的特点i的内存地址比j大,在Example函数结束后,i和j就会被释放,无法再访问 12 printf("i地址:%x,j地址:%x\n", &i, &j);// 比较两个内存地址大小 13 return &i; // 返回栈区地址,读取值的时候可以发现值不是0,说明内存被重新分配了。 14 } 15 16 int *ExampleHeap() 17 { 18 // 变量p本身在栈区,存储着malloc分配的内存首地址,使用*可以取地址的值,而*p在堆区 19 int *p = (int *)malloc(sizeof(int)); 20 // 堆特点,p比q先定义,p内存地址比q小 21 int *q = (int *)malloc(sizeof(int)); 22 printf("p地址:%x,q地址:%x\n", p, q);// 比较两个内存地址大小 23 *p = 20; 24 free(q); 25 q = NULL; 26 return p; // 返回变量p,读取值正常 27 } 28 29 void main() 30 { 31 int *p = ExampleStack(); 32 int *q = ExampleHeap(); 33 char *str = "abcd"; // str本身在栈区,而"abcd"在数据区 常量区,且str只读 34 printf("栈区:%d\n", *p); // 这里打印出的p是一个垃圾值,说明ExampleStack结束的时候已经把i的内存释放 35 printf("堆区:%d\n", *q); // 这里打印出20,说明变量q中的内存地址没有被释放 36 if (q != NULL) free(q); // 释放内存 37 q = NULL;// 防野指针 38 p = NULL;// 防野指针 39 system("pause"); 40 }
代码中除了主main函数还有ExampleStack和ExampleHeap函数作为示例,为方便作为例子还使用了指针,指针本身就不做过多介绍,不懂的话可以在了解指针知识后再来看这段代码。
本文只是总结了自己对C语言的一些认识,并不能直接当成教程使用,但可以作为初学者学习借鉴的知识来看。
转载于:https://www.cnblogs.com/yaoh/p/4133935.html
C/C++心得-从内存开始相关推荐
- 使用android studio查看内存,Android Studio Profiler使用心得 检测内存泄露问题
最近在分析app的内存泄露问题研究了下as的profiler功能 这里记录下个人心得 下面是个人心得 1.首先得出结论profiler不详细提供分析c或c++库进行的内存占用 其中包括android ...
- 【文献心得】内存隔离技术研究现状调研
文章目录 内存隔离相关技术 学术研究成果 工业嵌入式操作系统方案 参考文献 嵌入式系统的应用广泛,嵌入式操作系统对实时性和可靠性要求严苛,为了满足嵌入式操作系统对可靠性和实时性的要求,迫切需要一种能够 ...
- Android 如何有效的解决内存泄漏的问题
前言:最近在研究Handler的知识,其中涉及到一个问题,如何避免Handler带来的内存溢出问题.在网上找了很多资料,有很多都是互相抄的,没有实际的作用. 本文的内存泄漏检测工具是:LeakCana ...
- 【转帖】内存知识大全
内存是主板上重要的部件之一,它是存储CPU与外围设备沟通的数据与 程序的部件.在主机中,内存所存储的数据或程序有些是永久的,有些是暂时的,所以内存就有不同形式的功能与作用,而且存储数据的多少也关系着内 ...
- 内存回收在嵌入式系统应用方面的调研和总结
前传 嵌入式系统的内存回收还是比较重要的,因为这块涉及到程序运行性能. 嵌入式系统(比如平板,手机)会更加关注单机性能优化,因而会更加重视系统内存回收. 嵌入式系统不像互联网那种大型分布式服务器系统, ...
- Java并发编程之指令重排序
在我们面试过程中,通常避免不了会被问到什么是指令重排序?本文就这个问题进行探索. 重排序 前言 一.重排序种类 二.happens-before 三.重排序 1.数据依赖性 2. as-if-seri ...
- 无线路由升级内存和FLASH的几点心得
折腾路由玩,在升级内存和FLASH的过程中出现了很多问题,特此写下此心得 1.在升级路由前,一定要了解升级路由的硬件配置,以免出现硬件不支持的情况.(比如HG255D压根激活不了128M内存, ...
- linux按进程分配物理内存,linux下内存管理学习心得(一)
最近在学习内存管理的时候,发现对linux下的所谓内存如何管理如何分配都不熟悉,通过最近的查阅资料可总结如下,如有不妥之处欢迎大家批评与指正. 总的的来说linux的内存管理其实主要难理解的是以下几个 ...
- static在内存层面的作用_「C++ Primer plus 心得」9.内存模型和名称空间
本章内容包括: 单独编译 存储持续性.作用域和链接性 定位new运算符 名称空间 C++ 为在内存中存储数据方面提供了多种选择.可以厅数据保留在内存中的时间长度(存储持续性)以及程序的哪一部分可以访问 ...
- stm32qspi内存映射_STM32F7-discovery QSPI接口使用心得
STM32电机培训online,大佬带你玩电机 FMC和QSPI引脚冲突的解决 分享一个 QSPI N25Q256A的读写程序,支持QUAD, 4字节模式 1.QuadSPI接口的特点.与普通的SPI ...
最新文章
- 械模拟计算器 - 计算尺
- iconfont 图标转为字体_阿里字体库iconfont使用方法
- pycharm没有python interpreter_首次安装Pycharm出现No Python interpreter selected解决方法
- 【放置奇兵】算法 酒馆攻略
- 浅谈C/C++中的指针和数组(一)
- [Magento error] The url is not accessible, unable to read response
- Intel开发文档导读
- 注册围框html,一种可调模具围框的制作方法
- python邮件发送_Python实现邮件发送
- 二十一天学通C++之异常概述
- lisp把多段线顶点连成表_读取多段线顶点并将顶点坐标标到数组中
- 在光标位置处插入图片
- BS架构和CS架构的区别
- 能玩游戏的计算机推荐,电脑玩啥端游合适 适合长期玩的游戏有哪些
- 海外服务器配置多少带宽比较合适?
- 怎样在虚拟机装文件服务器,虚拟机下怎么解压文件
- 文件及文件夹删除失败的解决方法
- [Android]进程通信Andromeda框架
- OSChina 周六乱弹 —— 三口气印度史(3.5)
- 含有使字的诗句_古诗含“把”字
热门文章
- Milano Store OpenCart 2.0 主题模板 ABC-0473
- Android开发中如何加载API源码帮助开发
- Idea 工具在java文件中怎么避免 import .*包
- OC中的字符串转换为C中的字符串
- 首次主持春晚,她因太漂亮登上热搜
- 快手面试官:Redis变慢了,如何快速排查?
- 面试官问:MySQL的自增 ID 用完了,怎么办?
- Dubbo 的设计思想,真优秀!
- 独家下载 | 《Redis+Nginx+设计模式+Spring全家桶+Dubbo》,附 PDF 架构书籍 下载
- 2019上半年白领跳槽盘点:仅3成成功跳槽,17%降薪