阅读 72

数据管理(二)

Null指针

与MS-DOS不同,但是类似于新版本的Windows,现代的Linux系统会小心的处理读取或是写入由null指针所指向的地址,尽管实际的行为是实现相关的。

试验--访问Null指针

下面我们在memory5a.c程序中试一下当我们访问一个null指针时会发生什么情况:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
    char *some_memory = (char *)0;
    printf(“A read from null %s/n”, some_memory);
    sprintf(some_memory, “A write to null/n”);
    exit(EXIT_SUCCESS);
}

其输出如下:

$ ./memory5a
A read from null (null)
Segmentation fault(core dumped)

工作原理

第一个printf试图打印出由一个null指针处所取得的字符串;然后sprintf试图将一个字符串写入null指针处。在这种情况下,Linux(借助GNU C库)忽略了此处的读取,并且只是简单的返回给我们一个包含(null)/0字符的神奇字符串。但是他并没有忽略写入操作,并且结束了这个程序。这对于追踪程序bug是十分有用的。

如果我们再试一次,但是这次我们不使用GNU C库,我们就会发现由地址0处读取是不允许的。如下面的memory5b.c:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
    char z = *(const char *)0;
     printf(“I read from location zero/n”);
    exit(EXIT_SUCCESS);
}

其输出如下:

$ ./memory5b
Segmentation fault(core dumped)

工作原理

这一次我们试着直接由地址0处读取。但是这一次在我们与内核之间并不存在GNU libc库,但是我们程序被终止了。我们应该注意到某些版本的UNIX允许由地址0处读取,但是Linux并不允许这样做。

释放内存

直到现在,我们只是简单的分配内存,然后希望当程序结束时,我们所用的内存并没有丢失。幸运的是Linux内存管理系统具有可靠的能力可以保证当一个程序结束时其所用的内存会返回给系统。然而,绝大多数的程序并不只是希望简单的分配某块内存,使用一段时间,然后退出。一个更为普遍的用法是在需要时动态使用。

动态使用内存的程序应总是使用free调用释放不再使用的内存,从而将这些内存返回malloc内存管理器。这会使得单独的内存进行重新合并,并且使得malloc库来管理内存,而不是使得程序来管理内存。如果一个运行的程序(进程)使用然后释放内存,释放的内存会保持分配给进程。然而,如果他不再被使用,Linux内存管理器就可以将其由物理内存中换出保存到交换空间中,这对于资源的使用有一些小的影响。

#include <stdlib.h>
void free(void *ptr_to memory);

free调用只可以使用由malloc,calloc或是realloc调用所获得的内存指针来进行调用。我们很快就会遇到calloc与realloc。

试验--释放内存

此处程序名为memory6.c:

#include <stdlib.h>
#define ONE_K (1024)
int main()
{
    char *some_memory;
    int exit_code = EXIT_FAILURE;
    some_memory = (char *)malloc(ONE_K);
    if (some_memory != NULL) {
        free(some_memory);
        exit_code = EXIT_SUCCESS;
    }
    exit(exit_code);
}

工作原量

这个程序只是简单的演示了如何使用前面已分配内存的指针调用free函数。

记住一旦我们在某一个内存块上调用了free函数,那么他就不再属性于这个进程。他也不再被malloc库所管理。不要在调用free函数之后试图在此内存块上执行读写操作。

其他的内存分配函数

还有另外两个不如malloc与free这样经常使用的内存分配函数:calloc与realloc。其函数原型如下:

#include <stdlib.h>
void *calloc(size_t number_of_elements, size_t element_size);
void *realloc(void *existing_memory, size_t new_size);

尽管calloc分配的内存可以使用free函数调用来进行翻译,但是他却具有某些不同的参数:他为一个结构数据分配内存,并且需要元素个数以及每一个元素的尺寸作为其参数。分配的内存会使用0进行填充,如果calloc函数调用成功,则会返回一个指向第一个元素的指针。与malloc函数类似,其后的调用并不会返回连续的空间,所以我们不可能通过简单的再次调用calloc函数并且期望第二次调用所返回的内存添加到第一次调用的结束处,从而来扩大由calloc所分配的内存空间。

realloc会改变前一次分配的内存块的尺寸。调用此函数时会传递一个指向由malloc,calloc或是realloc函数所分配的内存的指针,并且根据要求调整其大小。realloc也许会需要能过移动数据来达到此目的,所以很重要的一点就是要保证一旦内存被重新分配了,我们必须使用新的指针,而不要试图使用realloc调用以前的指针来访问内存。

另一个需要关注的问题就是如果realloc不可以调整内存,他就会返回一个空指针。这就意味着,在某些程序中,我们应避免编写类似如下的代码:

my_ptr = malloc(BLOCK_SIZE);
....
my_ptr = realloc(my_ptr, BLOCK_SIZE * 10);

如果realloc函数调用失败,他就会返回一个空指针;my_ptr就会指向空;那么原始的由malloc所分配的内存就不可以使用my_ptr来访问了。很可能我们所需要做的是首先访问由malloc所分配的新内存,然后在翻译旧内存块之前使用memcpy函数将旧内存块的数据拷贝到新内存块。一旦出现错误,这可以使得程序重新访问存储在原始内存块中的数据,也许就是在执行程序清理的时候。

文章分类
代码人生
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐