重学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类型可能就得不出这样反应其映射性关系的结果,因为它们的解析完全不同。
内存四区
本文主要将内存,之前都是一些零碎的知识,现在具体来说说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 return &i; // 返回栈区地址,读取值的时候可以发现值不是0,说明内存被重新分配了。 13 } 14 15 int *ExampleHeap() 16 { 17 // 变量p本身在栈区,存储着malloc分配的内存首地址,使用*可以取地址的值,而*p在堆区 18 int *p = (int *)malloc(sizeof(int)); 19 *p = 20; 20 return p; // 返回变量p,读取值正常 21 } 22 23 void main() 24 { 25 int *p = ExampleStack(); 26 int *q = ExampleHeap(); 27 char *str = "abcd"; // str本身在栈区,而"abcd"在数据区 常量区,且str只读 28 printf("栈区:%d\n", *p); // 这里打印出的p是一个垃圾值,说明ExampleStack结束的时候已经把i的内存释放 29 printf("堆区:%d\n", *q); // 这里打印出20,说明变量q中的内存地址没有被释放 30 if (q != NULL) free(q); // 释放内存 31 q = NULL;// 防野指针 32 p = NULL;// 防野指针 33 system("pause"); 34 }
代码中除了主main函数还有ExampleStack和ExampleHeap函数作为示例,为方便作为例子还使用了指针,指针本身就不做过多介绍,不懂的话可以在了解指针知识后再来看这段代码。
本文只是总结了自己对C语言的一些认识,并不能直接当成教程使用,但可以作为初学者学习借鉴的知识来看。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。