我一直听说在C中你必须真正关注你如何管理记忆。 而且我还在开始学习C语言,但到目前为止,我根本不需要做任何内存管理相关活动。我总是想象必须释放变量并做各种丑陋的事情。 但事实似乎并非如此。

有人可以给我(带代码示例)一个例子,说明何时需要进行“内存管理”?

===============>>#1 票数:220 已采纳

有两个地方可以将变量放入内存中。 当您创建这样的变量时:

int  a;
char c;
char d[16];

变量在“ 堆栈 ”中创建。 当堆栈变量超出范围时(即代码无法再访问它们时),它们会自动释放。 你可能会听到它们被称为“自动”变量,但这已经过时了。

许多初学者示例仅使用堆栈变量。

堆栈很好,因为它是自动的,但它也有两个缺点:(1)编译器需要事先知道变量有多大,(b)堆栈空间有限。 例如:在Windows中,在Microsoft链接器的默认设置下,堆栈设置为1 MB,并非所有变量都可用。

如果你在编译时不知道你的数组有多大,或者你需要一个大的数组或结构,你需要“计划B”。

计划B称为“ ”。 您通常可以创建与操作系统允许的一样大的变量,但您必须自己完成。 早期的帖子向您展示了一种可以做到的方法,尽管还有其他方法:

int size;
// ...
// Set size to some value, based on information available at run-time. Then:
// ...
char *p = (char *)malloc(size);

(注意堆中的变量不是直接操作的,而是通过指针操作)

一旦你创建了一个堆变量,问题是编译器无法判断你何时完成它,所以你失去了自动释放。 这就是您所指的“手动释放”的用武之地。您的代码现在负责决定何时不再需要变量,并释放它以便将内存用于其他目的。 对于上述情况,使用:

free(p);

是什么让这第二个选择“令人讨厌的业务”是,知道何时不再需要该变量并不总是很容易。 忘记在不需要时释放变量将导致程序消耗更多内存。 这种情况称为“泄漏”。 在程序结束并且操作系统恢复其所有资源之前,“泄露”的内存不能用于任何事情。 如果你实际完成之前错误地释放了一个堆变量,那么即使是更糟糕的问题也是可能的。

在C和C ++中,您负责清理堆变量,如上所示。 但是,有些语言和环境(如Java和.NET语言,如C#)使用不同的方法,堆可以自行清理。 第二种方法,称为“垃圾收集”,对开发人员来说要容易得多,但是您需要在开销和性能方面付出代价。 这是一个平衡点。

(我已经掩盖了许多细节,以提供更简单,但希望更平坦的答案)

===============>>#2 票数:17

这是一个例子。 假设你有一个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 */

你现在已经耗尽了11 gig的内存(可能更多,取决于你的内存管理器),如果你没有崩溃你的进程可能运行得很慢。

要修复,您需要在完成使用后使用malloc()为所有内容调用free():

s = strdup("hello");
free(s);  /* now not leaking memory! */
s = strdup("world");
...

希望这个例子有帮助!

===============>>#3 票数:9

当你想在堆而不是堆栈上使用内存时,你必须做“内存管理”。 如果您不知道在运行时创建数组有多大,那么您必须使用堆。 例如,您可能希望将某些内容存储在字符串中,但在程序运行之前不知道其内容的大小。 在那种情况下你会写这样的东西:

 char *string = malloc(stringlength); // stringlength is the number of bytes to allocate

 // Do something with the string...

 free(string); // Free the allocated memory

===============>>#4 票数:5

我认为最简洁的方式来回答这个问题,考虑指针在C中的作用。指针是一个轻量级但功能强大的机制,以极大的能力为您提供巨大的自由,让您在脚下射击自己。

在C中,确保你的指针指向你拥有的记忆的责任在于你自己和你自己。 这需要一种有组织的,有纪律的方法,除非你放弃指针,这使得很难写出有效的C.

发布的日期答案集中在自动(堆栈)和堆变量分配上。 使用堆栈分配确实可以实现自动管理和方便的内存,但在某些情况下(大缓冲区,递归算法),它可能会导致堆栈溢出的可怕问题。 确切地知道您可以在堆栈上分配多少内存非常依赖于系统。 在某些嵌入式方案中,几十个字节可能是您的限制,在某些桌面方案中,您可以安全地使用兆字节。

堆分配不是语言固有的。 它基本上是一组库调用,授予您对给定大小的内存块的所有权,直到您准备返回(“释放”)它为止。 这听起来很简单,但与无尽的程序员悲伤有关。 问题很简单(释放相同的内存两次,或根本没有[内存泄漏],没有分配足够的内存[缓冲区溢出]等)但很难避免和调试。 高度严谨的方法在实践中是绝对必要的,但当然语言实际上并不强制要求它。

我想提一下其他帖子忽略的其他类型的内存分配。 通过在任何函数之外声明变量,可以静态地分配变量。 我认为通常这种类型的分配会得到一个糟糕的说法,因为它被全局变量使用。 然而,没有什么可以说使用这种方式分配内存的唯一方法是在一堆意大利面条代码中作为一个没有纪律的全局变量。 静态分配方法可以简单地用于避免堆和自动分配方法的一些缺陷。 一些C程序员惊讶地发现,大型复杂的C嵌入式和游戏程序的构建完全没有使用堆分配。

===============>>#5 票数:4

关于如何分配和释放内存,这里有一些很好的答案,在我看来,使用C的更具挑战性的一面是确保你使用的唯一内存是你已经分配的内存 - 如果这没有正确完成你的结束up with是这个站点的表兄弟 - 缓冲区溢出 - 你可能会覆盖另一个应用程序正在使用的内存,结果非常不可预测。

一个例子:

int main() {
    char* myString = (char*)malloc(5*sizeof(char));
    myString = "abcd";
}

此时你为myString分配了5个字节,并用“abcd \\ 0”填充它(字符串以null结尾 - \\ 0)。 如果您的字符串分配是

myString = "abcde";

您将在已分配给程序的5个字节中分配“abcde”,并且尾随的空字符将放在此末尾 - 未分配给您使用的内存的一部分,可能是免费,但同样可以被另一个应用程序使用 - 这是内存管理的关键部分,其中错误会产生不可预测的(有时是不可重复的)后果。

===============>>#6 票数:4

要记住的是始终将指针初始化为NULL,因为未初始化的指针可能包含伪随机有效内存地址,这会使指针错误无声地继续。 通过强制使用NULL初始化指针,您可以始终捕获是否使用此指针而不初始化它。 原因是操作系统将虚拟地址0x00000000“连线”到一般保护异常以捕获空指针使用。

===============>>#7 票数:2

当你需要定义一个庞大的数组时,你可能还想使用动态内存分配,比如int [10000]。 你不能把它放在堆栈中,因为那样,嗯......你会得到堆栈溢出。

另一个很好的例子是数据结构的实现,比如链表或二叉树。 我没有要在此处粘贴的示例代码,但您可以轻松地进行google。

===============>>#8 票数:2

(我写作是因为我觉得到目前为止答案并不是很明显。)

您需要提到内存管理的原因是当您遇到需要创建复杂结构的问题/解决方案时。 (如果你的程序一旦分配到堆栈上的大量空间就会崩溃,这就是一个错误。)通常,你需要学习的第一个数据结构是某种列表 这是一个单独的链接,在我的头顶:

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保证(通过语言标准)返回可被4整除的指针的事实,
  • 为你自己的某些险恶目的分配额外的空间,
  • 创建内存池 ..

得到一个好的调试器...祝你好运!

===============>>#9 票数:0

@Ted Percival
...你不需要强制转换malloc()的返回值。

当然,你是对的。 我相信这一直是真的,虽然我没有K&R的副本来检查。

我不喜欢C中的很多隐式转换,所以我倾向于使用强制转换来使“魔术”更加明显。 有时它有助于提高可读性,有时却没有,有时它会导致编译器捕获到无声错误。 不过,我对这方面的看法并不那么强烈。

如果您的编译器理解C ++风格的注释,这尤其可能。

是的...你抓住我了 我花了很多时间在C ++而不是C.感谢你注意到这一点。

===============>>#10 票数:0

@ Euro Micelli

要添加的一个消极因素是,当函数返回时,指向堆栈的指针不再有效,因此您无法从函数返回指向堆栈变量的指针。 这是一个常见错误,也是您无法使用堆栈变量的主要原因。 如果你的函数需要返回一个指针,那么你必须使用malloc并处理内存管理。

===============>>#11 票数:0

在C中,您实际上有两种不同的选择。 一,您可以让系统为您管理内存。 或者,你可以自己做。 一般来说,你会希望尽可能长时间地坚持前者。 但是,C中的自动管理内存非常有限,在许多情况下您需要手动管理内存,例如:

一个。 您希望变量比函数更长,并且您不希望拥有全局变量。 例如:

struct pair{
   int val;
   struct pair *next;
}

struct pair* new_pair(int val){
   struct pair* np = malloc(sizeof(struct pair));
   np->val = val;
   np->next = NULL;
   return np;
}

你想拥有动态分配的内存。 最常见的例子是没有固定长度的数组:

int *my_special_array;
my_special_array = malloc(sizeof(int) * number_of_element);
for(i=0; i

c. You want to do something REALLY dirty. For example, I would want a struct to represent many kind of data and I don't like union (union looks soooo messy):

struct data{ int data_type; long data_in_mem; }; struct animal{/*something*/}; struct person{/*some other thing*/}; struct animal* read_animal(); struct person* read_person(); /*In main*/ struct data sample; sampe.data_type = input_type; switch(input_type){ case DATA_PERSON: sample.data_in_mem = read_person(); break; case DATA_ANIMAL: sample.data_in_mem = read_animal(); default: printf("Oh hoh! I warn you, that again and I will seg fault your OS"); }

看,长值足以容纳任何东西。 只要记得释放它,否则你会后悔的。 这是我最喜欢在C:D中获得乐趣的技巧。

但是,一般来说,你会希望远离你最喜欢的技巧(T___T)。 如果你经常使用它们,你迟早会破坏你的操作系统。 只要你不使用* alloc和free,就可以说你还是处女,并且代码看起来还不错。

===============>>#12 票数:-2

当然。 如果你创建一个存在于你使用它的范围之外的对象。这是一个人为的例子(请记住我的语法将关闭;我的C生锈,但这个例子仍然会说明这个概念):

class MyClass
{
   SomeOtherClass *myObject;

   public MyClass()
   {
      //The object is created when the class is constructed
      myObject = (SomeOtherClass*)malloc(sizeof(myObject));
   }

   public ~MyClass()
   {
      //The class is destructed
      //If you don't free the object here, you leak memory
      free(myObject);
   }

   public void SomeMemberFunction()
   {
      //Some use of the object
      myObject->SomeOperation();
   }


};

在这个例子中,我在MyClass的生命周期中使用了SomeOtherClass类型的对象。 SomeOtherClass对象用于多个函数中,因此我动态分配了内存:SomeCther对象是在创建MyClass时创建的,在对象的生命周期中多次使用,然后在释放MyClass后释放。

显然,如果这是真正的代码,那么就没有理由(除了可能的堆栈内存消耗)以这种方式创建myObject,但是当你有很多对象并想要精确控制时,这种类型的对象创建/破坏会变得很有用当它们被创建和销毁时(例如,你的应用程序在其整个生命周期内不会占用1GB的RAM),并且在Windowed环境中,这几乎是强制性的,因为你创建的对象(按钮,比方说) ,需要在任何特定函数(甚至是类)范围之外存在。

  ask by The.Anti.9 translate from so

未解决问题?本站智能推荐:

3回复

C内存管理->队列

好的,所以我有这个可以执行的Queue实现,但是我遇到了一些内存泄漏,该泄漏不允许在此之后运行其他操作。 文件queue_arr.h 文件queue_arr.c 最后的if and else语句不起作用,我注意到原因是我试图执行isEmpty if子句,并一直导致我出现分段
1回复

C语言中的内存管理问题

我有以下C函数,这使我遇到很多内存问题,如果我不释放内存,它可以正常工作,但是当我调用另一个函数时,我得到了malloc内存损坏错误,但是我释放了它,我得到了自由无效指针。 最后,由于strtok工作异常,我为fecha和fechaVariable定义了新的指针时,我收到了rupted_siz
2回复

C / C ++功能的内存管理

假设有一个具有全局功能的cpp文件。 据我所知,指令集是在我运行程序时复制到内存中的,并保留在那里直到程序结束。 每次我调用一个函数时,变量的虚拟地址都是一样的,但是我不明白它首先分配内存,并且直到程序结束或每次调用函数时才分配内存才释放。 编辑:我在除main之外的其他函数中调用
1回复

如何管理C中函数返回的内存

我想制作一个像这样工作的标记子程序: 读取用户输入 找到令牌 使用子字符串函数来获取每个标记 将每个标记存储在结构中 这个想法很简单(我想),但我提出了一个难题。 在创建子串函数时,我意识到它是一个内存泄漏漏洞。 我能想到创建子字符串函数的唯一方法是
3回复

C中的位域内存管理

为了理解位字段存储器,我在下面创建了测试程序。 编译并运行 当我在线运行代码http://codepad.org/ntqyuixp时输出相同 我无法理解它的输出。 预期输出:根据我的理解,输出应该是这样的 请让我知道我错过了什么。 字节序是否起任何
2回复

在C中返回字符串时管理内存

我有一个我一直在Visual Studio Express 2013中编写的CGI应用程序,但是遇到一种方案,当传递给函数的字符串超过31个字节时,程序将失败。 我会尝试自己调试它,但是在Visual Studio Debugger中一切正常,仅在命令提示符中看到错误。 我相信这是我分配
4回复

Java和C如何管理局部变量的内存?

我正在考虑Java和C如何在范围内管理变量。 在Java中,每个迭代器都创建一个新对象,然后将其打印出来。 结果说,每个迭代器我都有一个新的Object。 还有C语言中的scope变量。我做的事情与Java中的一样。 每个迭代器我都创建一个变量并打印其地址。 但是结果使
1回复

C用int指针(数组)进行内存管理?

简单的问题,但是这里的其他类似问题都无法解决这种特定情况,所以我可以找到。 更改指针后,应该如何释放移动的内存?
8回复

C的智能指针/安全内存管理?

我和其他许多人一样,使用智能指针在C ++中包含不安全的内存操作,使用RAII等等,已经取得了巨大的成功。 但是,当您具有析构函数,类,运算符重载等时,包装内存管理更容易实现。 对于使用原始C99编写的人,您可以在哪里指出(没有双关语)来帮助安全内存管理? 谢谢。
2回复

C Lib设计 - 结构和内存管理[最佳实践] [关闭]

所以,我是一个尝试学习C的C#人。作为第一个(个人)项目,我试图编写一个基本的坐标几何库。 问题:在幕后的堆上分配内存而不是让以图书馆为目标的程序员这样做是不是最好的C编程实践呢? 例如,我的'point'结构和相关方法: point.h point.c 请注意