本文是2020年广州大学Linux操作系统实验指导书中的实验三,由于实验指导书是Word文档,代码格式显示很乱,不管是看起来还是复制都很不和谐。本文改善了实验指导书的体验,对代码格式以及文字段落等都做了优化,让你可以专注于做实验本身,提高实验效率。

实验三的实验教程已经写完,根据实验指导书的代码修改成可以直接编译运行的代码(点击下方链接即可查看)。


目录

一、实验目的

二、实验内容

三、实验指导

四、实验步骤

一、实验目的

1、了解静态内存与动态内存的区别;

2、理解动态内存的分配和释放原理;

3、掌握如何调整动态内存的大小;

4、利用链表实现动态内存分配。

二、实验内容

1、利用malloccalloc函数实现动态内存的分配;利用free函数实现动态内存的释放;

2、利用realloc函数实现调整内存空间的大小;

3、利用链表实现动态内存分配。

三、实验指导

1、静态内存与动态内存

按分配内存空间的方式不同,一个程序所使用的内存区域可以分为静态内存与动态内存。在程序开始运行时有系统分配的内存称为静态内存,在程序运行过程中由用户自己申请分配的内存称为动态内存。

静态内存的申请是由编译器来分配的。对于用户程序中的各种变量,编译器在编译源程序时处理了为各种变量分配所需内存的工作。当程序执行时,系统就为变量分配所需的内存空间,至使用该变量的函数执行完毕返回时,自动释放所占用的内存空间。使用静态内存对用户来说是很方便的。用户并不需要了解分配内存的具体细节,也不需要时刻考虑由于程序结束前未释放所占用的内存空间而带来的可用内存泄漏。同时,静态内存也是不通过指针而使用变量的唯一方法。但是,静态内存也存在一定的缺陷:首先,由于静态内存总是预先定义了存放数据的数组大小,这就有可能因为所传入的数据量大于数组容量而引发的溢出问题。或因为定义了一个大数组,而所传入的数据量远小于数组容量,而对内存空间造成浪费。

其次,由于在某个函数中分配的静态内存将在此函数运行结束时被系统自动释放,使用指针由子函数向主函数传递数据的设想是无法被实现的。

使用动态内存时,用户可以根据需要随时申请内存,使用完毕后手动将此内存区释放。在实际应用中非常方便。但动态内存的使用也存在着巨大的隐患。任何处理过大型项目的用户都知道动态内存的使用会使内存管理变得多么复杂,以及要确切地记得在使用完毕后释放所占用的内存空间是多么困难的事情。在大型应用程序中,由于在释放某块动态内存前将指向该内存区域的指针重新赋值,从而使得此内存区域无法被释放的情况是十分常见的。通常将内存分配后没有被释放而导致可用内存减少称之为内存泄漏。避免内存泄漏耗尽系统资源正是许多服务器每隔一段时间就需要重新启动的原因。另外还要注意,由于分配动态内存时,用户得到的是一块void类型的内存,用户可以将其作为任何类型的内存空间使用,也可能引发一些无法预计的结果。

2、动态内存的分配

分配动态内存空间所使用的函数调用如下:

#include<stdio.h>
void malloc(size_t size);
void
calloc(size_t nmemb, size_t size);

函数malloccalloc都是用于分配动态内存空间的函数。

函数malloc的参数size表示申请分配的内存空间的大小,以字节计。

函数calloc的参数nmemb表示申请分配的内存空间占的数据项数目,参数size表示一个数据项的大小,以字节计。也就是说,calloc函数分配大小为nmemb*size大小的内存空间。

函数calloc与函数malloc的最大区别就是函数calloc将初始化所分配的内存空间,把所有位置0。调用成功时,函数calloc与函数malloc的返回值都为被分配的内存空间的指针;调用失败时,返回值为NULL

3、动态内存的释放

释放动态内存空间所使用的函数调用如下:

#include<stdio.h>
void free(void ptr);

此函数的作用是释放由函数calloc或函数malloc分配的动态内存。参数ptr是指向要释放的动态内存的指针。

注意:当动态内存被释放后,原来指向它的指针就会变为悬空指针。此时使用该指针将会产生错误。

下面的例子就是使用动态内存实现将子函数中分配的内存空间指针返回主函数,实现数据的传递。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <string.h>

char upcase(char inputstring);

int main(void) {
char
str1, str2;

str1=upcase("Hello" );
str2=capitao("Goodbye");

printf("str1=%s, str2=%s\n", str1, str2);

free(str1);
free(str2);

return 0;
}


char
upcase(char inputstring) {
char
newstring;
int counter;

if(!(newstring=malloc(strlen(inputstring)+1))){
printf("ERROR ALLOCATING MEMORY! \n");
exit(255);
}
strcpy(newstring, inputstring);
for(counter=0; counter<strlen(newstring); counter++){
if(newstring[counter]>=97&&newstring[counter]<=122)
newstring[counter]-=32;
}
return newstring;
}

在这个程序中由于使用了动态内存,使得子函数可以灵活地分配所需的内存空间。

4、调整动态内存的大小

对于用函数calloc与函数malloc分配好的动态内存,可以使用realloc函数来调整它的大小。该函数的说明如下:

#include<stdio.h>
voidrealloc(void ptr, size_t size);

realloc函数的作用是重新调整一块动态内存区域的大小。

参数ptr是指向要调整的动态内存的指针,应是函数calloc与函数malloc的返回值。
参数size是新定义的动态内存的大小。Size可以大于或小于动态内存的原大小,调用realloc函数时,通常是在原来的内存空间调整动态内存的大小,原有数据不被改动。当size大于原大小,而原位置中无法完成调整时,将重新开辟内存空间并将原数据拷贝到新的内存空间中。

注意:如果参数ptrNULL,则函数realloc的作用相当于函数malloc。如果参数size0,则函数realloc的作用相当于函数free

下面的例子说明了该函数的应用:在这个程序中,针对函数upcase的参数newstring是否为NULL,采取了不同的处理方式。如果newstring不为NULL,则直接分配内存空间。否则调整原空间的大小以适应新的需要。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <string.h>

void upcase(char inputstring, char newstring);

int main(void) {
char string;

upcase("Hello",string);
printf("str1=%s \n", string);
capitao("Goodbye", string);
printf("str2=%s\n", string);

free(string);

return 0;
}


void upcase(char
inputstring, char *newstring) {
int counter;

if(!newstring) {
if(!(newstring=realloc(NULL, strlen(inputstring)+1))) {
printf("ERROR ALLOCATING MEMORY! \n");
exit(255);
}
} else {
if(!(newstring=realloc(newstring, sizeof(inputstring)+1))) {
printf("ERROR REALLOCATING MEMORY! \n");
exit(255);
}
}
strcpy(newstring, inputstring);
for(counter=0; counter<strlen(newstring); counter++) {
if(newstring[counter]>=97&&newstring[counter]<=122)
newstring[counter]-=32;
}
return ;
}

5、使用链表进行动态内存的分配

虽然使用动态内存可以方便地使用内存,但动态内存也有局限性,就是在数据输入到程序之前必须知道数据的大小,以便申请相应的动态内存。然而,在很多情况下,用户都无法事先知道这个值,因而也就无法申请相应的内存空间。对于这种事先未知大小的数据输入,可以使用链表将其分块保存。

链表是一种动态地进行存储分配的结构。链表中的各个元素是一个结构,每个元素称为链表的一个结点。此结构中包含有一个指向此结构的指针,用于指向链表中的下一个结点。链表的最后一个结点的指针NULL,表示链表结束。

下面的例子说明了使用链表获得动态内存的方法:这个程序将终端输入的一系列字符串用链表的形式保存下来。然后再将这些数据组装起来,回显到输出终端。链表的结点为stringdata结构。stringdata结构中的整型量iscontinuing用于表示当前结点是否为链表的末尾。如果iscontinuing有值,则表示此结点不是链表的末尾。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define DATASIZE 10

typedef struct stringdata {
char string;
int iscontinuing;
struct stringdata
next;
} mydata;

mydata append(mydata start, char input);
void displaydata(mydata
start);
void freedata(mydata start);

int main(void) {
char input[DATASIZE];
mydata
start=NULL;

printf("ENTER SOME DATA,AND PRESS Ctrl+D WHEN DONE. \n");

while(fgets(input, sizeof(input), stdin)) {
start=append(start, input);
}

displaydata(start);
freedata(start);
return 0;
}

mydata append(mydata start, char input) {
mydata
cur=start, prev=NULL, new;

while(cur) {
prev=cur;
cur=cur->next;
}
cur=prev;

new=malloc(sizeof(mydata));
if(!new) {
printf("COULDN’T ALLOCATE MEMORY! \n");
exit(255);
}

if(cur)
cur->next=new;
else
start=new;

cur=new;

if(!(cur->string=malloc(sizeof(input)+1))) {
printf("ERROR ALLOCATING MEMORY! \n");
exit(255);
}
strcpy(cur->string, input);
cur->iscontinuing=!(input[strlen(input)-1]=='\n'||input[strlen(input)-1]=='\r');
cur->next=NULL;

return start;
}


void displaydata(mydata start) {
mydata
cur;
int linecounter=0, structcounter=0;
int newline=1;

cur=start;
while(cur) {
if(newline)
printf("LINE %d:",++linecounter);
structcounter++;
printf("%s",cur->string);
newline=!cur->iscontinuing;
cur=cur->next;
}
printf("THIS DATA CONTAINED %d LINES AND WAS STORED IN %d STRUCTS. \n",
linecounter,structcounter);
}


void freedata(mydata start) {
mydata
cur, *next=NULL;

cur=start;
while(cur) {
next=cur->next;
free(cur->string);
free(cur);
cur=next;
}
}

四、实验步骤

1、利用malloc函数和free函数来实现动态内存的申请和释放;

2、利用realloc函数来修改上述malloc函数申请的动态内存的大小;

3、利用链表结构,malloc函数和free函数实现将终端输入的一系列字符串用链表的形式保存下来。然后再将这些数据组装起来,回显到输出终端。

最后修改日期:2020年5月14日

留言

撰写回覆或留言