本章主要内容
本章开启了虚拟化主题的第二个话题,内存虚拟化,简单介绍了内存虚拟化的概念以及为啥要进行内存虚拟化,后面介绍了几个UNIX环境下的内存操作的库函数,总体来说比较容易理解,感兴趣的可以去阅读该书。
第一题
1.编写一个名为null.c的简单程序,它创建一个指向整数的指针,将其设置为NULL,然后尝试对其进行释放内存操作。把它编译成一个名为null的可执行文件。当你运行这个程序时会发生什么?
C语言中NULL为0的宏定义
#include <stdlib.h>
int main(int argc, char **argv)
{
int *pi = NULL;
free(pi);
return 0;
}
nothing happen.
第二题
2.编译该程序,其中包含符号信息(使用-g 标志)。这样做可以将更多信息放入可执行文件中,使调试器可以访问有关变量名称等的更多有用信息。通过输入gdb null,在调试器下运行该程序,然后,一旦gdb运行,输入run。gdb显示什么信息?
编译程序:
gcc null.c -o null -g
运行信息(程序正常退出):
第三题
3.对这个程序使用valgrind工具。我们将使用属于valgrind的memcheck工具来分析发生的情况。输入以下命令来运行程序:valgrind --leak-check=yes null。当你运行它时会发生什么?你能解释工具的输出吗?
这道题需要用到一个工具,valgrind,关于如何安装网上有许多教程,参考一下就可以了。
我的平台是Ubuntu20.04,需要指定null程序的路径./null
执行如下命令:
valgrind --leak-check=yes ./null
输出信息:
输出信息还是很清晰的,说明在程序退出的时候,堆上的内存都已经释放干净了,没有内存泄露的可能。
第四题
4.编写一个使用malloc()来分配内存的简单程序,但在退出之前忘记释放它。这个程序运行时会发生什么?你可以用gdb来查找它的任何问题吗?用valgrind呢(再次使用–leak-check=yes标志)?
写bug,我还是很擅长的:)
#include <stdlib.h>
int main(int argc, char **argv)
{
int *pi = (int *)malloc(sizeof(int));
return 0;
}
程序运行表现的似乎是正常的,看不出来有问题。
使用gdb调试:
使用gdb这样调试也是正常退出,因为程序并没有在运行时发生错误。
使用valgrind调试:
从上面的信息就很容易看出,程序是存在内存泄漏的,总共是4个字节,也就是我的机器上int的大小。
完了,我的bug被发现了:( ,valgrind果然强大。
第五题
5.编写一个程序,使用malloc()创建一个名为data、大小为100的整数数组。然后,将data[100]设置为0。当你运行这个程序时会发生什么?当你使用valgrind运行这个程序时会发生什么?程序是否正确?
数组的大小是100,在计算机中下标是从0开始的,所以这里对100下标赋值显然是不合法的。
//over.c
#include <stdlib.h>
int main(int argc, char **argv)
{
int *array = (int *)malloc(sizeof(int) * 100);
array[100] = 0;
return 0;
}
首先直接运行./over
,竟然没有错误发生···
使用valgrind运行:
这里提示程序有两处错误,一处是非法写入的问题,还有一个就是内存泄露的问题。
这里为了显示更详细的错误信息,在运行时加入-s
标志
第六题
6.创建一个分配整数数组的程序(如上所述),释放它们,然后尝试打印数组中某个元素的值。程序会运行吗?当你使用valgrind时会发生什么?
这个直观上会发生悬挂指针的问题,就是提前释放掉还在使用的指针。
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int *array = (int *)malloc(sizeof(int) * 100);
free(array);
printf("%d", array[66]);
return 0;
}
使用valgrind运行分析:
错误信息显示,程序存在非法读取的问题。
后面的几道题就不做了,和上面的都差不多,主要是体会一下在进行内存操作时有可能给程序带来潜在问题的操作,上面的程序在运行时都表现不出来错误,但是error却真实存在。
stdlib中还提供了几个其他的申请内存的函数:
calloc会初始化申请的内存
realloc可以对现在的内存进行扩展,将旧的的内存复制到新的内存中并返回该内存的头指针