我一直听说在C语言中必须非常注意如何管理内存。虽然我还在开始学习C,但到目前为止,我还没有进行过任何与内存管理相关的活动。我一直想象要释放变量并做各种丑陋的事情。但似乎情况并不是这样。
有人能给我展示(附带代码示例),什么情况下需要进行“内存管理”?
变量可以存储在内存的两个位置。当您创建一个变量时,例如:
int a;
char c;
char d[16];
int size;
// ...
// Set size to some value, based on information available at run-time. Then:
// ...
char *p = (char *)malloc(size);
free(p);
以下是一个例子。假设您有一个strdup()函数,它可以复制一个字符串:
char *strdup(char *src)
{
char * dest;
dest = malloc(strlen(src) + 1);
if (dest == NULL)
abort();
strcpy(dest, src);
return dest;
}
然后你这样调用它:
main()
{
char *s;
s = strdup("hello");
printf("%s\n", s);
s = strdup("world");
printf("%s\n", s);
}
你可以看到程序在运行,但你已经使用malloc分配了内存却没有释放。当你第二次调用strdup时,你失去了对第一个内存块的指针。
对于这么少量的内存来说这不是什么大问题,但考虑以下情况:
for (i = 0; i < 1000000000; ++i) /* billion times */
s = strdup("hello world"); /* 11 bytes */
现在您已经使用了11GB的内存(根据您的内存管理器,可能更多),如果您没有崩溃,那么您的进程可能运行得非常缓慢。
要解决这个问题,您需要在使用完malloc()获取的所有内容后调用free():
s = strdup("hello");
free(s); /* now not leaking memory! */
s = strdup("world");
...
希望这个例子能够帮助您!当您需要在堆上使用内存而不是栈上时,必须进行“内存管理”。如果在运行时之前不知道要创建多大的数组,则必须使用堆。例如,您可能想要将某些内容存储在字符串中,但在程序运行之前不知道其内容的大小。在这种情况下,您可以编写如下代码:
char *string = malloc(stringlength); // stringlength is the number of bytes to allocate
// Do something with the string...
free(string); // Free the allocated memory
int main() {
char* myString = (char*)malloc(5*sizeof(char));
myString = "abcd";
}
在这一点上,你已经为myString分配了5个字节并用"abcd\0"填充它(字符串以空字符 - \0 结尾)。 如果你的字符串分配是
myString = "abcde";
strcpy()
而不是=
;我认为那是克里斯·B-C的意图。 - echristopherson一个需要记住的事情是要始终将指针初始化为NULL,因为未初始化的指针可能包含一个伪随机的有效内存地址,这可能会导致指针错误默默地继续执行。通过强制指针使用NULL进行初始化,您可以始终捕获是否在未初始化的情况下使用该指针。原因是操作系统将虚拟地址0x00000000“连接”到一般保护异常以捕获空指针使用。
typedef struct listelem { struct listelem *next; void *data;} listelem;
listelem * create(void * data)
{
listelem *p = calloc(1, sizeof(listelem));
if(p) p->data = data;
return p;
}
listelem * delete(listelem * p)
{
listelem next = p->next;
free(p);
return next;
}
void deleteall(listelem * p)
{
while(p) p = delete(p);
}
void foreach(listelem * p, void (*fun)(void *data) )
{
for( ; p != NULL; p = p->next) fun(p->data);
}
listelem * merge(listelem *p, listelem *q)
{
while(p != NULL && p->next != NULL) p = p->next;
if(p) {
p->next = q;
return p;
} else
return q;
}
需要补充的一点是,当函数返回时,指向堆栈的指针将不再有效,因此您不能从函数中返回指向堆栈变量的指针。这是一个常见的错误,也是您不能仅使用堆栈变量的主要原因。如果您的函数需要返回指针,则必须使用malloc并处理内存管理。