• 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语言的一些认识,并不能直接当成教程使用,但可以作为初学者学习借鉴的知识来看。

  • 相关阅读:
    设计模式-----简单工厂模式
    LeetCode题解002:两数相加
    LeetCode题解001:两数之和
    异常处理类-Throwable源码详解
    Windows下 gcc/g++的安装与配置
    Windows10下安装解压版MySQL教程
    Windows下Django项目搭建流程
    Linux域名服务DNS
    Linux文件共享服务 FTP,NFS 和 Samba
    操作系统:进程的概念和与程序的区别
  • 原文地址:https://www.cnblogs.com/yaoh/p/4133935.html
Copyright © 2020-2023  润新知