一起学习C语言:初谈指针(二)

  上一篇<一起学习C语言:初谈指针(一)> 中,我们了解了指针变量的声明和初始化方式,以及指针变量的引用赋值方式。本篇文章中,我们初步分析不同的内存空间 ,并通过示例来了解动态内存的分配、使用和释放方式。

章节预览:


4. 首谈进程中的内存区域
5. 指针变量存储字符串常量
6. 如何动态分配内存空间
目录预览

章节内容:


4. 首谈进程中的内存区域

  当程序执行时,程序将会以进程的形式存在虚拟内存中,系统会为我们的进程分配不同的内存空间。我们的进程就像一个大箱子,程序定义的对象放置在这个箱子中的某个区域中的某个位置。接下来,我们初次了解进程中的不同内存空间:

    代码段:英文名称code segment / text segment,通常用来存放程序执行代码的内存区域。这部分区域的大小在程序加载时就已经确定,并且在大部分情况下内存区域通常属于只读状态(某些架构允许代码段为可写状态)。在代码段中,宏、函数存储在这个区域,也有可能存储一些常数量。另外,常数量的值不允许修改。

    数据段:英文名称 data segment,通常存放程序中已初始化的全局变量的内存区域。数据段属于静态内存分配,其中包括全局变量、静态变量和常数量。关于全局变量和静态变量的内容,将在“函数”章节中讲述。

    bss段:英文名称bss segment,通常用来存放程序中未初始化的全局变量的内存区域。bss属于静态内存分配,在程序执行之前bss段自动清零。

    栈区:英文名称stack,通常用来存放程序中临时创建的局部变量的内存区域。栈属于动态内存储存区,栈的内存由函数执行期间申请,在函数执行结束后自动归还。

    堆区:英文名称heap,通常用来存放程序中临时被动态分配的数据内存区域。堆属于动态内存分配区,堆的内存在需要时随时开辟,不需要时随时释放。堆中的内存与以上几种内存不同,堆中的内存由内核分配、回收,属于进程共享内存,但自己的进程不能访问别的进程堆区,由内核做了限制。而以上几种内存属于进程独有的,由进程分配、回收。需要注意的是,堆内存虽由内核分配、回收,但也在进程中记录和管理,当进程退出时,未释放的堆内存通常会被回收,但也存在未被回收的情况。


5. 指针变量存储字符串常量

  在“数组”章节中,我们了解了字符串由多个连续的有效字符组成,并以‘\0’为结尾。在实际编程中,如果字符串内容不需要修改,可以通过指针变量存储字符串常量。

  【例7.4】 判断一个字符串常量的长度,并输出字符串常量中的每个字符。

      char *p = “123456”;
      int len = sizeof(“123456”);
      int i = 0;

      for (; i < len; i++) {
          printf(“字符%d,值为%c.\n”, i, *(p + i));
      }

      示例结果

          字符0,值为1.
          字符1,值为2.
          字符2,值为3.
          字符3,值为4.
          字符4,值为5.
          字符5,值为6.
          字符6,值为 .

      示例分析

          1. 定义char类型指针变量p,并初始化赋值字符串常量的地址;
          2. 定义int类型变量len,并初始化赋值为字符串常量的长度。这里只能使用sizeof (6) 获取字符串常量的长度,而不是指针变量的长度。
          3. 输出的最后一个字符为空字符(‘\0’)。


  (6):sizeof只能用来获取“直接访问”内存的字节长度,不能用来获取指针变量指向的内存字节长度。 这里只是作为示例使用,主要为了看出字符串常量的最后一个字节为空字符(‘\0’)。实际编程中,应使用strlen函数获取字符串长度,strlen遇到空字符结束,可以理解为strlen得到的是字符串减去空字符的长度,即有效字符的长度。示例中,int len = sizeof(“123456”)可以修改为int len = strlen( p),len的值为6。


6. 如何动态分配内存空间

  之前我们使用的是由进程分配、回收的内存,在部分情况下,已不能满足使用。比如定义一个1MB (7) 的数组char a[1024 * 1024],程序执行时会提示“Stack overflow”等相关的“栈溢出”信息。


  (7):在计算机中,一般由内存单位表示内存空间大小。比如bit表示1位、Byte表示1字节、1KB表示1024字节、1MB表示1024 * 1024字节、1GB表示1024 * 1024 * 1024字节,1TB表示1024 * 1024 * 1024 * 1024字节。
          在硬件工程方面还有小b和大B的说法,小b由内存单位和英文小写b组成,表示位级单位。比如1Kb表示1024位、1Mb表示1024 * 1024位等。大B由内存单位和英文大写B组成,表示字节级单位。我们前面已经了解到1Byte等于8bit,也可以轻松理解1Kb等于128B(Byte),1MB等于8Mb这种转换方式。
          计算机内存一般按照1024划分单位区域,而硬件容量一般按照1000划分单位区域。比如1T硬盘挂载后,只有931GB左右内存空间。


  接下来,我们通过系统提供的库函数,了解动态内存的分配和释放方式。


    1. malloc函数

        函数原型:void * malloc(unsigned int size); (8)
        函数描述:用来分配一块长度为size的连续内存空间
        函数参数:size的类型为无符号整型
        函数返回值:内存分配成功将返回所分配区域的第一个字节的地址,分配失败则返回NULL


  【例7.5】 定义int类型指针变量,并为指针变量动态分配4个int类型长度。

      int *p = (int *)malloc(sizeof(int) * 4);

      if (NULL != p)
          printf(“p地址为%p.”, p);
      else
          printf(“内存分配失败.”);


      示例结果

          p地址为01B08A58.


      示例分析

          如果malloc函数返回的是一个非NULL的地址,表示内存分配成功。示例中,成功分配到16个字节。


  (8):void属于无类型,不能单独作为变量类型使用。编译器支持C99或以上允许使用void *,即空类型指针量 (9) 或不确定的类型指针量。在实际编程中,void *必须转换到确定类型后,才能进行对内存空间赋值。确定类型包括基本数据类型、结构体等,结构体将在后面章节讲述。

  (9):计算机中的内存不按类型区分,只是在使用时根据类型划分区域。比如16个字节内存按int类型赋值,相当于划分为4个int类型内存空间,但从内存的角度来考虑,这是一整块内存空间。


    2. free函数

        函数原型:void free(void * memory);
        函数描述:用来释放指针memory指向的内存空间,使这块内存空间可以再次被分配使用
        函数参数:memory的类型为空类型指针量
        函数返回值:无


  【例7.6】 定义int类型常量指针,为常量指针动态分配4个int类型长度并分别赋值1、2、3、4,然后释放内存。

      int * const p = (int *)malloc(sizeof(int) * 4); (10)
      int i = 0;

      for (; NULL != p && i < 4; i++) {
          *(p + i) = i + 1; (11)
          printf(“p的地址:%p, int%d的地址 %p 值:%d.\n”, p, i + 1, p + i, *(p + i));
      }

      if (NULL != p)
          free( p);


      示例结果

          p的地址:01228A58, int1的地址 01228A58 值:1.
          p的地址:01228A58, int2的地址 01228A5C 值:2.
          p的地址:01228A58, int3的地址 01228A60 值:3.
          p的地址:01228A58, int4的地址 01228A64 值:4.


      示例分析

          1. 使用动态内存时,需要确定内存地址有效并且在指向的内存空间范围内,再使用这块内存。如果不这么做,可能会通过无效内存地址访问到未分配或超出范围的内存空间,导致不可预估的错误并造成程序崩溃。
          2. 释放动态内存时,也需要确定内存地址有效(属于我们动态创建的内存),这即属于良好的编程习惯,也可以保证程序不会崩溃。


  (10):关于常量指针这部分内容,目前只做了解。这部分内容篇理论性(稍难理解),将在“再谈指针”章节中讲述。

  (11):示例中的p + i可以看做是p + 0、p + 1、p + 2、p + 3,即每次向后偏移一个变量类型地址。示例中,p属于int类型常量指针,p + 1等于向后偏移1个int空间地址,即p的地址向后偏移4个字节。如果p属于char类型指针变量或常量指针,p + 1等于向后偏移1个char空间地址,即p的地址向后偏移1个字节。


目录预览


<一起学习C语言:C语言发展历程以及定制学习计划>
<一起学习C语言:初步进入编程世界(一)>
<一起学习C语言:初步进入编程世界(二)>
<一起学习C语言:初步进入编程世界(三)>
<一起学习C语言:C语言数据类型(一)>
<一起学习C语言:C语言数据类型(二)>
<一起学习C语言:C语言数据类型(三)>
<一起学习C语言:C语言基本语法(一)>
<一起学习C语言:C语言基本语法(二)>
<一起学习C语言:C语言基本语法(三)>
<一起学习C语言:C语言基本语法(四)>
<一起学习C语言:C语言基本语法(五)>
<一起学习C语言:C语言循环结构(一)>
<一起学习C语言:C语言循环结构(二)>
<一起学习C语言:C语言循环结构(三)>
<一起学习C语言:数组(一)>
<一起学习C语言:数组(二)>
<一起学习C语言:数组(三)>
<一起学习C语言:初谈指针(一)>

猜你喜欢

转载自blog.csdn.net/a29562268/article/details/106205087