06-内存管理
1. 内存管理
1.1 内存管理
了解内存是 C 语言编程的一个重要方面。当你使用基本数据类型声明一个变量时,C 语言会自动在一个叫做栈的内存区域为该变量分配空间。
例如,一个 int
变量,在声明时通常分配4个字节。我们通过使用 sizeof 运算符获取某个变量的字节大小:
int x;
printf("%d", sizeof(x)); /* output: 4 */
#include <stdio.h>
int main() {
int x;
printf("%d", sizeof(x)); /* output: 4 */
return 0;
}
再比如,一个指定大小的数组分配在一个连续的内存块,每个块的大小为一个元素的大小:
int arr[10];
printf("%d", sizeof(arr)); /* output: 40 */
#include <stdio.h>
int main() {
int arr[10];
printf("%d", sizeof(arr)); /* output: 40 */
return 0;
}
只要你的程序明确声明了基本数据类型或数组大小,内存就会自动管理。然而,你可能经希望实现一个数组大小在运行时才动态分配大小的程序。
动态内存分配是根据需要分配和释放内存的过程。现在你可以在运行时提示数组元素的数量,然后创建一个指定大小的数组。
动态内存是用指针管理的,指针指向一个叫做堆的区域中新分配的内存块。
除了使用堆栈的自动内存管理和使用堆的动态内存分配外,在主内存中还有静态管理的数据,用于在程序的生命周期内持续存在。
1.2 动态内存分配是?
A. 根据需要分配和释放内存的过程
B. 定义数组的过程
C. 声明函数指针的过程
A
1.3 内存管理函数
stdlib.h 库包括内存管理函数。 在你的程序顶部的语句#include <stdlib.h>
能够访问以下函数:
malloc(bytes)
返回一个指向连续内存块的指针,该内存块的大小为bytes。calloc(num_items, item_size)
返回一个指向具有 num_items项的连续内存块的指针,每个项的大小为item_size字节。通常用于数组,结构和其他派生数据类型。分配的内存被初始化为0。realloc(ptr, bytes)
将ptr
指向的内存大小调整为字节大小。新分配的内存未初始化。free(ptr)
释放ptr
指向的内存块。
当你不再需要一个分配的内存块时,使用函数
free()
释放,之后该内存块可再次分配使用。
填空,引入可访问内存管理函数的头文件:
___include <___.h>
#
stdlib
2. malloc 和 free 函数
2.1 malloc 函数
malloc() 函数在内存中分配指定数量的连续字节。
例如:
#include <stdlib.h>
int *ptr;
/* a block of 10 ints */
ptr = malloc(10 * sizeof(*ptr));
if (ptr != NULL) {
*(ptr + 2) = 50; /* assign 50 to third int */
}
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
ptr = malloc(10*sizeof(*ptr)); /* a block of 10 ints */
if (ptr != NULL) {
*(ptr+2) = 50; /* assign 50 to third int */
}
printf("3rd elem equals to %d", *(ptr + 2));
return 0;
}
填空,分配 10 个字节的大小的内存:
int* ptr = ___(10);
malloc
2.2 malloc 函数
分配内存是连续的,可以作为一个数组对待。不使用括号[ ]
来访问元素,而是使用指针运算来遍历数组。建议你使用+
来迭代数组元素。使用 ++
或 +=
可以更改指针所存储的地址。
如果分配不成功,则返回 NULL。因此,你应该包含用于检查 NULL 指针的代码。
一个简单的二维数组需要
(rows*columns)*sizeof(datatype)
字节的内存。
分配的内存为:
A. 随机分布的
B. 连续的
B
2.3 free 函数
free() 函数是一个内存管理函数,它用来释放内存。通过释放内存,可以腾出更多的内存供以后的程序使用。
例如:
int* ptr = malloc(10 * sizeof(*ptr));
if (ptr != NULL)
*(ptr + 2) = 50; /* assign 50 to third int */
printf("%d\n", *(ptr + 2));
free(ptr);
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
ptr = malloc(10*sizeof(*ptr)); /* a block of 10 ints */
if (ptr != NULL)
*(ptr+2) = 50; /* assign 50 to third int */
printf("%d\n", *(ptr+2)); /* 50 */
free(ptr);
return 0;
}
填空,分配内存,然后释放由 malloc()
分配的内存:
int ___ ptr = malloc(10);
___ (ptr);
*
free
3. calloc 和 realloc 函数
3.1 calloc 函数
calloc()
函数根据特定项(例如结构体)的大小分配内存。
下面的程序使用 calloc
为结构分配内存,并使用 malloc
为结构中的字符串分配内存:
typedef struct {
int num;
char *info;
} record;
record *recs;
int num_recs = 2;
int k;
char str[ ] = "This is information";
recs = calloc(num_recs, sizeof(record));
if (recs != NULL) {
for (k = 0; k < num_recs; k++) {
(recs+k)->num = k;
(recs+k)->info = malloc(sizeof(str));
strcpy((recs+k)->info, str);
}
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int num;
char *info;
} record;
int main() {
record *recs;
int num_recs = 2;
int k;
char str[ ] = "This is information";
recs = calloc(num_recs, sizeof(record));
if (recs != NULL) {
for (k = 0; k < num_recs; k++) {
(recs+k)->num = k;
(recs+k)->info = malloc(sizeof(str));
strcpy((recs+k)->info, str);
}
}
for (k = 0; k < num_recs; k++) {
printf("%d\t%s\n", (recs+k)->num, (recs+k)->info);
}
return 0;
}
calloc 在一个连续的内存块为结构体元素的数组分配内存块。你可以用指针运算从一个结构体指向到下一个结构体。
在为一个结构体分配空间后,必须为结构体内的字符串分配内存。
动态分配的结构是链表和二叉树以及其他数据结构的基础。
填空,为 3 个 recssord
结构分配内存:
typedef struct {
int num;
char *info;
} record;
___ *recs;
recs = ___(3, ___(record))
record
calloc
sizeof
3.2 realloc 函数
realloc(void *__ptr, size_t __size)
:更改已经配置的内存空间,即更改由 malloc()
函数分配的内存空间的大小。
如果将分配的内存减少,realloc
仅仅是改变索引的信息。
如果是将分配的内存扩大,则有以下情况:
1)如果当前内存段后面有需要的内存空间,则直接扩展这段内存空间,realloc()
将返回原指针。
2)如果当前内存段后面的空闲字节不够,那么就使用堆中的第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,并将原来的数据块释放掉,返回新的内存块位置。
3)如果申请失败,将返回 NULL,此时,原来的指针仍然有效。
int *ptr;
ptr = malloc(10 * sizeof(*ptr));
if (ptr != NULL) {
*(ptr + 2) = 50; /* assign 50 to third int */
}
ptr = realloc(ptr, 100 * sizeof(*ptr));
*(ptr + 30) = 75;
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
ptr = malloc(10*sizeof(*ptr)); /* a block of 10 ints */
if (ptr != NULL) {
*(ptr+2) = 50; /* assign 50 to third int */
}
ptr = realloc(ptr, 100*sizeof(*ptr)); /* 100 ints */
*(ptr+30) = 75;
printf("%d %d", *(ptr+2), *(ptr+30));
return 0;
}
realloc 将原始内容保留在内存中,并扩展该块以允许更多存储。
下面代码的输出是哪一项?
int *arr = malloc(sizeof(int));
*arr = 13;
arr = realloc(arr, 2*sizeof(int));
*(arr + 1) = *arr;
printf("%d", *(arr + 1));
A. 0
B. 13
C. 未知
D. 14
B
4. 动态字符串及数组
4.1 为字符串分配内存
在为字符串指针分配内存时,你可能需要使用字符串长度而不是 sizeof
运算符来计算字节。
例如:
char str20[20];
char *str = NULL;
strcpy(str20, "12345");
str = malloc(strlen(str20) + 1);
strcpy(str, str20);
printf("%s", str);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char str20[20];
char *str = NULL;
strcpy(str20, "12345");
printf("str20 size: %d\n", sizeof(str20));
printf("str20 length: %d\n", strlen(str20));
str = malloc(strlen(str20)+1); /* make room for \0 */
strcpy(str, str20);
printf("%s", str);
return 0;
}
这种方法可以更好地管理内存,因为你分配的空间不会超出指针所需的空间。
当使用 strlen
确定字符串所需的字节数时,请确保为 NULL 字符 \0
添加一个额外的字节。
char 始终是一个字节,因此无需将内存需求乘以 sizeof(char)
。
填空,为 str 字符串分配 42 个字节:
___ *str = NULL;
str = ___ (42);
char
malloc
4.2 动态数组
许多算法实现了动态数组,因为这允许元素的数量根据需要增加。
由于不会一次分配所有元素,因此动态数组通常使用一种结构来跟踪当前数组的大小,当前容量以及指向元素的指针,如以下程序所示:
typedef struct {
int *elements;
int size;
int cap;
} dyn_array;
dyn_array arr;
/* initialize array */
arr.size = 0;
arr.elements = calloc(1, sizeof(*arr.elements) );
arr.cap = 1; /* room for 1 element */
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int *elements;
int size;
int cap;
} dyn_array;
int main() {
dyn_array arr;
int i;
/* initialize array */
arr.size = 0;
arr.elements = calloc(1, sizeof(*arr.elements));
arr.cap = 1; /* room for 1 element */
/* expand by 5 more elements */
arr.elements = realloc(arr.elements, (5 + arr.cap)*sizeof(*arr.elements));
if (arr.elements != NULL)
arr.cap += 5; /* increase capacity */
/* add an element and increase size */
if (arr.size < arr.cap) {
arr.elements[arr.size] = 50; /* add element to array */
arr.size++;
}
else
printf("Need to expand array.");
/* display array elements */
for (i = 0; i < arr.cap; i++)
printf("Element %d: %d\n", i, arr.elements[ i ]);
return 0;
}
要扩展更多元素:
arr.elements = realloc(arr.elements, (5 + arr.cap) * sizeof(*arr.elements));
if (arr.elements != NULL)
arr.cap += 5; /* increase capacity */
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int *elements;
int size;
int cap;
} dyn_array;
int main() {
dyn_array arr;
int i;
/* initialize array */
arr.size = 0;
arr.elements = calloc(1, sizeof(*arr.elements));
arr.cap = 1; /* room for 1 element */
/* expand by 5 more elements */
arr.elements = realloc(arr.elements, (5 + arr.cap)*sizeof(*arr.elements));
if (arr.elements != NULL)
arr.cap += 5; /* increase capacity */
/* add an element and increase size */
if (arr.size < arr.cap) {
arr.elements[arr.size] = 50; /* add element to array */
arr.size++;
}
else
printf("Need to expand array.");
/* display array elements */
for (i = 0; i < arr.cap; i++)
printf("Element %d: %d\n", i, arr.elements[ i ]);
return 0;
}
向数组添加元素会增加其大小:
if (arr.size < arr.cap) {
arr.elements[arr.size] = 50;
arr.size++;
} else {
printf("Need to expand the array.");
}
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int *elements;
int size;
int cap;
} dyn_array;
int main() {
dyn_array arr;
int i;
/* initialize array */
arr.size = 0;
arr.elements = calloc(1, sizeof(*arr.elements));
arr.cap = 1; /* room for 1 element */
/* expand by 5 more elements */
arr.elements = realloc(arr.elements, (5 + arr.cap)*sizeof(*arr.elements));
if (arr.elements != NULL)
arr.cap += 5; /* increase capacity */
/* add an element and increase size */
if (arr.size < arr.cap) {
arr.elements[arr.size] = 50; /* add element to array */
arr.size++;
}
else
printf("Need to expand array.");
/* display array elements */
for (i = 0; i < arr.cap; i++)
printf("Element %d: %d\n", i, arr.elements[ i ]);
return 0;
}
为了演示,整个程序写在 main()
中。为了正确实现动态数组,应该把子任务分解成 init_array()
、increase_array()
、 add_element()
和 display_array()
等函数。为了保持演示的简短,错误检查也被跳过了。
填空,为 int
数组分配内存,然后为更多元素扩展内存。
___* arr = malloc(4 * sizeof(int));
arr = ___(arr, 8 * sizeof(int));
int
realloc
5. 小测验
5.1 练习-1
填空,分配 10 字节内存:
int* mem = ___(___);
malloc
10
5.2 练习-2
为值 42 的整数分配内存,并打印:
int* ptr = ___(sizeof(int));
___= 42;
printf("%d", *ptr);
malloc
*ptr
5.3 练习-3
使用 calloc()
函数为 2 个 time 结构体分配内存:
typedef struct {
int minutes;
int hours;
} time;
time *tm = ___(2, sizeof___));
calloc
time
5.4 练习-4
填空,为一个 20 个字符的字符串分配内存,并为该字符串赋值:
char* str ____ malloc(20);
strcpy(___, "Hello, SoloLearn!");
=
str
5.5 练习-5
填空,创建并初始化一个动态数组:
int* arr = ___(10 * sizeof(int));
for (int i = 0; i < 10; ++i) {
___(arr + i) = i;
}
malloc
*
公众号:AI悦创【二维码】

AI悦创·编程一对一
AI悦创·推出辅导班啦,包括「Python 语言辅导班、C++ 辅导班、java 辅导班、算法/数据结构辅导班、少儿编程、pygame 游戏开发、Linux、Web、Sql」,全部都是一对一教学:一对一辅导 + 一对一答疑 + 布置作业 + 项目实践等。当然,还有线下线上摄影课程、Photoshop、Premiere 一对一教学、QQ、微信在线,随时响应!微信:Jiabcdefh
C++ 信息奥赛题解,长期更新!长期招收一对一中小学信息奥赛集训,莆田、厦门地区有机会线下上门,其他地区线上。微信:Jiabcdefh
方法一:QQ
方法二:微信:Jiabcdefh
