在C中, 本地变量存储在进程的stack内存区域.
当函数执行完后, 函数体内的本地变量占用的内存会自动释放掉.
在Linux中使用ulimit -a可以查看到stack的限制, 也可以通过/etc/security/limits.conf设置.
[root@db-172-16-3-150 zzz]# ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 204800
max locked memory (kbytes, -l) 50000000
max memory size (kbytes, -m) unlimited
open files (-n) 131072
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 10240
cpu time (seconds, -t) unlimited
max user processes (-u) 131072
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
由于stack区域的内存大小比较小, 所以适合存放临时性的变量. 不适合存放大量的需要hold在内存中的数据.
大数据或者不可预知的数据或者是需要在函数执行完后还需要HOLD在内存中的数据, 可以放到HEAP中。
但是HEAP中的数据与STACK不同,不会自动释放,也没有自动的垃圾回收机制。
如使用malloc申请的内存需要使用free释放, 否则如果没有任何指针指向这块内存区域后, 这块内存将无法回收, 直到进程退出 .
所以建议不用的内存要用free释放掉. 否则容易造成内存泄漏.
另外就是有一个工具valgrind可以用来侦察内存泄漏.
malloc语法
#include <stdlib.h>
void *malloc(size_t size);
// 返回一个空指针. 因为空指针可以存放任何类型的指针数据.
malloc() allocates size bytes and returns a pointer to the allocated memory. The memory is not cleared.
malloc与free是一对, free语法 :
void free(void *ptr);
free() frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(),
calloc() or realloc(). Otherwise, or if free(ptr) has already been called before, undefined behaviour occurs.
If ptr is NULL, no operation is performed.
用法举例 :
场景参照上一篇linked list数据结构的例子.
[root@db-172-16-3-150 zzz]# cat g.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct island {
char *name;
char *opens;
char *closes;
struct island *next;
} island;
void display(island *start)
{
island *i = start;
for (; i != NULL; i = i->next) {
fprintf(stdout, "Name: %s open: %s - %s\n", i->name, i->opens, i->closes);
}
}
island* create(char *name)
{
island *i = malloc(sizeof(island));
i->name = strdup(name); // strdup调用了malloc,并且将字符串复制到heap区域, 返回指针.
i->opens = "09:00";
i->closes = "17:00";
i->next = NULL;
return i;
}
void release(island *start) {
island *i = start;
island *next = NULL;
for (; i != NULL; i=next) {
next = i->next;
free(i->name);
free(i);
}
}
int main() {
island *start = NULL;
island *i = NULL;
island *next = NULL;
char name[80];
for(; fgets(name,80,stdin) != NULL; i = next) {
next = create(name);
if (start == NULL)
start = next ;
if (i != NULL)
i->next = next;
}
display(start);
release(start);
return 0;
}
执行结果 :
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./g.c -o g && ./g
// 输入
a
b
c
d
// 输入 CTRL+D
// 输出
Name: a
open: 09:00 - 17:00
Name: b
open: 09:00 - 17:00
Name: c
open: 09:00 - 17:00
Name: d
open: 09:00 - 17:00
其他 :
1. strdup
#include <string.h>
char *strdup(const char *s);
The strdup() function returns a pointer to a new string which is a duplicate of the string s. Memory for the
new string is obtained with malloc(3), and can be freed with free(3).
2. 在释放struct在heap的空间之前, 必须先通过这个struct存储的指向heap中字符串的指针去释放这串strdup拷贝到heap的内存区域. 如果struct先释放, 那这块字符串区域将没有任何指针指向它, 也无法回收. 造成内存泄漏.
3. 内存泄漏举例 :
[root@db-172-16-3-150 zzz]# cat g.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct island {
char *name;
char *opens;
char *closes;
struct island *next;
} island;
void display(island *start)
{
island *i = start;
for (; i != NULL; i = i->next) {
fprintf(stdout, "Name: %s open: %s - %s\n", i->name, i->opens, i->closes);
}
}
island* create(char *name)
{
island *i = malloc(sizeof(island));
i->name = strdup(name);
i->opens = "09:00";
i->closes = "17:00";
i->next = NULL;
return i;
}
void release(island *start) {
island *i = start;
island *next = NULL;
for (; i != NULL; i=next) {
next = i->next;
// 注释掉这行,也就是不回收调用strdup时申请的内存空间. free(i->name);
free(i);
}
}
int main() {
island *start = NULL;
island *i = NULL;
island *next = NULL;
char name[80];
for(; fgets(name,80,stdin) != NULL; i = next) {
next = create(name);
if (start == NULL)
start = next ;
if (i != NULL)
i->next = next;
}
display(start);
release(start);
return 0;
}
// 使用gcc -g 编译参数时, 编译的可执行文件包含了 debug 信息. 便于valgrind跟踪时打印详细的信息
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./g.c -o g && valgrind --leak-check=full ./g
==2166== Memcheck, a memory error detector
==2166== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==2166== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==2166== Command: ./g
==2166==
// 输入
a
b
c
d
// 输入 CTRL+D
Name: a
open: 09:00 - 17:00
Name: b
open: 09:00 - 17:00
Name: c
open: 09:00 - 17:00
Name: d
open: 09:00 - 17:00
==2166==
==2166== HEAP SUMMARY:
==2166== in use at exit: 12 bytes in 4 blocks
==2166== total heap usage: 8 allocs, 4 frees, 140 bytes allocated
==2166==
==2166== 12 bytes in 4 blocks are definitely lost in loss record 1 of 1
==2166== at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==2166== by 0x345FA798C1: strdup (in /lib64/libc-2.5.so)
==2166== by 0x400725: main (g.c:23)
==2166==
==2166== LEAK SUMMARY:
==2166== definitely lost: 12 bytes in 4 blocks
==2166== indirectly lost: 0 bytes in 0 blocks
==2166== possibly lost: 0 bytes in 0 blocks
==2166== still reachable: 0 bytes in 0 blocks
==2166== suppressed: 0 bytes in 0 blocks
==2166==
==2166== For counts of detected and suppressed errors, rerun with: -v
==2166== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)
3. valgrind为什么可以跟踪内存泄漏呢?
因为valgrind自己有malloc和free函数, 并且拦截了进程的malloc和free操作, 使用它自己的malloc和free替代. 进行跟踪.
因此等进程退出的时候, 可以发现有哪些HEAP中的内存是没有被free掉的.
4. 特别需要注意在heap中的变量, 在修改引用这个地址的指针时, 要先释放掉前面的指针值, 再赋予新的值。否则老的那块内存区域就内存泄漏了.