C语言——内存的思考

来源:互联网 发布:mac ariana grande 编辑:程序博客网 时间:2024/05/17 09:10

对于内存的思考

数据段和堆

  • 就像个堆栈段能够根据需要自动增长一样,数据段页包含了一个对象,用于完成这个任务,这就是堆(heap)。
  • 堆区域用于动态分配的存储,也就是通过malloc()函数获得内存,并通过指针访问。堆中所有的东西都是匿名的,
    不能按照名字直接访问,只能通过指针间接访问。从堆中获取内存的唯一办法就是通过调用malloc(以及同类的calloc、realloc等)库函数。
    calloc函数和malloc类似,但它在返回指针之前先把分配好的内存内容清零。

    realloc函数改变一个指针所指向的内存块的大小。
  • 堆内存的回收不必与它所分配的顺序一致(甚至可以不回收),所以无序的malloc/free最终会产生堆碎片。
  • 堆的末端由一个break的指针来标识。当堆需要更多内存时,可以通过系统调用brk和sbrk来移动break指针。
    你的程序可能无法同时调用malloc()和brk()。如果你使用malloc,malloc希望当你调用brk和sbrk时,它具有
    唯一的控制权。由于sbrk向进程提供了唯一的方法将数据段内存返回给系统内核,所以如果使用了malloc就有效的
    防止了程序的数据段缩小的可能性。

    要想获得以后能够返回给系统内核的内存,可以使用mmap系统调用来映射/dev/zero文件。需要返回这种内存时,
    可以使用munmap系统调用。

内存泄漏

C语言通常并不使用垃圾回收器,这些C程序在使用malloc()和free()时,堆常会出现两种类型问题:* 释放或改写正在使用的内存(称为“内存损坏”)。* 未释放不再使用的内存(称为“内存泄漏”)。

使用alloca()可以避免内存泄漏,当离开调用alloca的函数时,它所分配的内存自动释放(不提倡!)

检测内存泄漏

  1. 使用swap、netstat、vmstat命令观察是否有内存不断被分配且从不释放。
  2. 使用”pa -lu 用户名“命令来显示所有进程大小,查看是否有进程不断增长而从不缩小

总线错误

常见运行错误:
`bus error(core dumped) 总线错误(信息已转储)`
`segmentation fault(core dumped) 段错误(信息已转储)`

总线错误

  • 总线错误几乎都是由于未对齐的读或写引起的。之所以称为“总线错误”是因为出现未对齐的内存访问请求时,被堵塞的组件就是
    地址总线。对齐(alignment)意思是数据项只能存储在地址是数据项大小的整数倍的内存位置上。例如访问一个8字节的double
    数据使,地址只能是8的整数倍(24,8008等,但是1006不行)。
    一个会引起总线错误的小程序:

    union {
    char a[10];
    int i;
    }u;

    int *p = (int *)&(u.a[1]);
    *p = 11;

    这将导致一个总线错误,因为数组和int的联合确保数组a是按照int的4字节对齐的,所以”a+1“地址肯定未按int对齐。然后
    我们试图往这个地址存储4个字节的数据,但是这个访问只是按照单字节char对齐的,这就违反了规则。

段错误


  • 段错误是由于内存单元(负责额支持虚拟内存的硬件)的异常所致。该异常则通常是由于解除引用一个未初始化或非法值的指针引起,
    如果指针引用一个并不位于你的地址空间的地址,操作系统便会进行干涉。例如:

    int *p = 0;

    *p = 11;

如果未初始化的指针恰好具有未对齐的值(对于指针所要访问的数据而言),它将会产生总线错误而非段错误,因为CPU先看到地址
然后再将其发送给MMU。

  • 通常导致段错误的几个直接原因:

    1. 解除引用一个包含非法值的指针。
    2. 解除引用一个空指针(常常由于从系统程序中返回空指针,并未经检查就使用)。
    3. 在未得到正确的权限时进行访问。例如,试图往一个只读文本段存储值。
    4. 用光了堆栈或者堆空间。
  • 以发生频率为序,导致段错误的编程错误:

    1. 坏指针值错误:在指针赋值前就用它引用内存,或者想函数库传送一个坏指针;对指针释放后再访问它的内容。可修改free语句
      在指针释放之后再将其置空:

      free(p); p = NULL;
    2. 改写(overwrite)错误:越过数组边界写入数据,在动态分配内存两端之外写入数据,或改写一些堆管理数据结构

      p = malloc(256); p[-1] = 0; p[256] = 0;
    3. 指针释放引起的错误:释放同一个内存两次;释放一卡未曾malloc分配的内存;或释放使用中的内存,或者释放一个无效指针。

      如何在链表中释放元素

      在遍历链表时正确释放元素的方法是使用临时变量存储下一个元素的地址。这样就可以安全的在任何时候释放当前元素,不必担心在
      取下一个元素的地址时还要引用它:

      struct node *p, *start, *tmp;
      for(p = start; p;p = tmp)
      {
      tmp = p -> next;
      free(p);
      }
  • 0 0
    原创粉丝点击