C语言:内存管理
内存四区
一个C\C++编译的程序占用内存的四个区:
- 栈区(stack):由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
- 堆区(heap): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事。
- 数据区(Data):主要包括静态全局区和常量区,如果要站在汇编角度细分的话还可以分为很多小的区。全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量。 4、代码区(Code):存放函数体的二进制代码。
- 代码区(Code):存放函数体的二进制代码。
栈(stack):向下生长(填坑)
堆(heap):向上生长(柴火)
Heap、stack生长方向和内存存放方向是两个不同概念
内存四区和函数调用变量传递:一个单进程主程序有n个函数组成,C/C++编译器只会分配一个堆区,一个栈区。
内存相关函数
C 语言为内存的分配和管理提供了几个函数。这些函数可以在 <stdlib.h> 头文件中找到。
calloc()
分配 num 个长度为 size 的连续空间,并返回一个指向它的指针。calloc 会将分配内存地址上的值初始化为0。
void *calloc(int num, int size);
参数
- num:分配的元素个数
- size:元素的大小
返回:返回一个指针,指向以分配的内存,如果请求失败,则返回 NULL
#include<stdio.h> #include <stdlib.h> int main(void) { int i, n; int *a; printf("要输入元素的个数:"); scanf("%d", &n); // 因为calloc返回的是指针,所以要用指针去接,因为a是整型的还要强制转换成整型指针, // 分配 n*sizeof(int) 的内存大小 a = (int *) calloc(n, sizeof(int)); printf("输入%d个数字", n); for (i = 0; i < n; i++) { scanf("%d", &a[i]); // 格式化输入后面的形参需要 指针变量a的地址 } printf("输入的数字为:"); for (i = 0; i < n; i++) { printf("%d", a[i]); } free(a); // 释放内存 return 0; }
malloc()
分配size个字节的内存空间(这块内存空间在函数执行完成后不会被初始化),并返回一个指向它的指针。
void *malloc(size_t size)
参数
- size:字节,内存块的大小
返回:返回一个指针 ,指向已分配大小的内存。如果请求失败,则返回 NULL。
realloc()
重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。把内存扩展到 newsize
void *realloc(void *ptr, size_t size)
参数
- ptr:malloc、calloc 或 realloc 返回的指针
- size:字节,内存块的新大小
返回:返回一个指针,指向重新分配大小的内存
free()
释放之前调用 calloc、malloc 或 realloc 所分配的内存空间。
void free(void *ptr)
参数
- ptr:指针,指向一个要释放内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果传递的参数是一个空指针,则不会执行任何动作。
#include <stdio.h> #include <string.h> #include <stdlib.h> int main() { char *str; /* 最初的内存分配 */ // 分配15字节内存,返回指向它的指针 str = (char *) malloc(15); str = (char *) malloc(15 * sizeof(char)); // 一般这么写,一目了然 strcpy(str, "Never"); printf("String = %s, Address = %p\n", str, str); // String = Never, Address = 0000000000701440 /* 重新分配内存 */ str = (char *) realloc(str, 25 * sizeof(char)); // 重新分配25字节内存,返回指向它的指针 strcat(str, ".Ling"); printf("String = %s, Address = %p\n", str, str); // String = Never.Ling, Address = 0000000000701440 free(str); return (0); }
memcpy()
从存储区(地址上的数据;指针指向的地址上的数据) str2 复制 n 个字节到存储区 str1。
void *memcpy(void *str1, const void *str2, size_t n)
参数
- str1:目标数组
- str2:数据源
- n:字节数
返回:返回一个指向目标存储区 str1 的指针
#include <stdio.h> #include <string.h> int main () { const char src[20] = "Never.Ling"; char dest[20]; memcpy(dest, src, strlen(src)+1); printf("dest = %s\n", dest); //dest = Never.Ling return(0); }
将 s 中第 11 个字符开始的 6个连续字符复制到 d 中:
#include <stdio.h> #include<string.h> int main() { char *s="Never.Ling"; // s指向字符串的首地址 char d[20]; memcpy(d, s+6, 4);// 从第 6 个字符(r)开始复制,连续复制 4 个字符 d[4]='\0'; printf("%s", d); // Ling return 0; }
memmove()
void *memmove(void *str1, const void *str2, size_t n)
从 str2 复制 n 个字节到 str1
在重叠内存块这方面,memmove() 是比 memcpy() 更安全的方法。如果目标区域和源区域有重叠的话,memmove() 能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后源区域的内容会被更改。如果目标区域与源区域没有重叠,则和 memcpy() 函数功能相同。
#include <stdio.h> #include <string.h> int main (){ const char dest[] = "oldstring"; const char src[] = "newstring"; printf("dest = %s, src = %s\n", dest, src); // dest = oldstring, src = newstring memmove(dest, src, 9); printf("dest = %s, src = %s\n", dest, src);// dest = newstring, src = newstring return(0); }
memset()
void *memset(void *str, int c, size_t n)
复制字符 c(一个无符号字符)到 str 所指向 指针的前 n 个字符。常用于清除内存数据。
#include <stdio.h> #include <string.h> int main (){ char str[20]; strcpy(str,"Never.Ling"); puts(str); // Never.Ling memset(str, '$', 3); // memset(str, 0, sizeof(str)); // 清除内存位置 puts(str); // $$$er.Ling return(0); }
动态分配内存
注意:void * 类型表示未确定类型的指针。C、C++ 规定 void * 类型可以通过类型转换强制转换为任何其它类型的指针。
编程时,如果您预先知道数组的大小,那么定义数组时就比较容易。例如,一个存储人名的数组,它最多容纳 100 个字符,所以您可以定义数组,如下所示:
char name[100];
但是,如果我们预先不知道需要存储的文本长度,在这里,我们需要定义一个指针,该指针指向未定义所需内存大小的字符,后续再根据需求来分配内存,如下所示:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char name[100]; char *description; strcpy(name, "Zara Ali"); /* 动态分配内存 */ description = (char *)malloc( 200 * sizeof(char) ); strcpy( description, "Zara ali a DPS student in class 10th"); printf("Name = %s\n", name ); // Name = Zara Ali printf("Description: %s\n", description ); // Description: Zara ali a DPS student in class 10th }
上面的程序也可以使用 calloc() 来编写,只需要把 malloc 替换为 calloc 即可,如下所示:
calloc(200, sizeof(char));
当动态分配内存时,您有完全控制权,可以传递任何大小的值。而那些预先定义了大小的数组,一旦定义则无法改变大小。
相关文章
- 如何面向对象编程?程序员:我也要先有“对象”啊
- 设计一个可扩展的用户登录系统 (3)
- 设计一个可扩展的用户登录系统 (2)
- 彪悍的职业不惧阿尔法狗
- 沉默的大多数
- 在Ubuntu Server上编译FFmpeg
- 素食,跑步,修行
- 2016厦门马拉松胜利完赛
- 秋游红叶岭
- 如何完成一个全程马拉松
- 设计一个可扩展的用户登录系统 (1)
- 愤怒的小怪兽
- 推荐:七周七并发模型
- 当你遇到一个巨大的代码库,你应该怎么办
- Clojure驱动的Web开发
- 给Nginx配置一个自签名的SSL证书
- 如何正确地处理时间
- 如何在一个页面上让多个jQuery版本共存
- npm install canvas简明指南
- 不使用UINavigationController实现Push动画