为什么函数可以通过malloc返回数组设置,但不能通过“int cat = {0,0,0};”返回一个设置。

为什么我可以通过malloc从函数返回数组设置:

int *dog = (int*)malloc(n * sizeof(int)); 

但不是由数组设置

  int cat[3] = {0,0,0}; 

返回“cat []”数组并显示警告。

感谢你的帮助

正在运行的程序中有两个关键的内存部分: 堆栈堆栈也称为调用堆栈

进行函数调用时,有关参数,返回位置以及函数范围中定义的所有变量的信息将被压入堆栈。 (过去的情况是C变量只能在函数的开头定义。主要是因为它使编译器编写者的生活更轻松。)

从函数返回时,堆栈中的所有内容都会弹出并消失(很快当你进行更多的函数调用时,你会覆盖那个内存,所以你不想指向它!)

无论何时分配内存,都要从堆中分配。 这是内存的其他部分,由分配管理器维护。 一旦你“保留”它的一部分,你就要对它负责,如果你想停止指向它,你应该让经理知道。 如果你放下指针而不能再让它释放,那就是泄漏

你也应该只看你想要的记忆部分。 不仅覆盖你想要的部分,而且过去(或之前)部分内存是一种经典的漏洞利用技术:将信息写入存储计算机指令而不是数据的内存部分。 了解编译器和运行时如何管理事物有助于专家了解如何执行此操作。 精心设计的操作系统阻止他们这样做。

堆:

 int *dog = (int*)malloc(n*sizeof(int*)); 

堆:

 int cat[3] = {0,0,0}; 

这是一个范围问题。

 int cat[3]; // declares a local variable cat 

局部变量与malloc内存

堆栈上存在局部变量。 当此函数返回时,这些局部变量将被销毁。 此时,用于存储arrays的地址将被回收,因此您无法保证其内容的任何内容。

如果你调用malloc,你将从堆中分配,因此内存将持续超出函数的生命周期。

如果该函数应该返回一个指针(在这种情况下,指向整数数组的第一个地址指针),该指针应该指向良好的内存。 Malloc是确保这一点的方法。

避免Malloc

您不必在函数内部调用malloc(尽管这样做是正常和适当的)。

或者,您可以将一个地址传递给您应该保存这些值的函数。 你的函数将完成计算值的工作,并将填充给定地址的内存,然后它将返回。

实际上,这是一种常见的模式。 但是,如果这样做,您会发现不需要返回地址,因为您已经知道正在调用的函数之外的地址。 因此,返回一个表示例程成功或失败的值(比如int)更常见,而不是返回相关数据的地址。

这样,函数的调用者可以知道数据是否已成功填充或是否发生错误。

 #include  // include stdio for the printf function int rainCats (int *cats); // pass a pointer-to-int to function rainCats int main (int argc, char *argv[]) { int cats[3]; // cats is the address to the first element int success; // declare an int to store the success value success = rainCats(cats); // pass the address to the function if (success == 0) { int i; for (i=0; i<3; i++) { printf("cat[%d] is %d \r", i, cats[i]); getchar(); } } return 0; } int rainCats (int *cats) { int i; for (i=0; i<3; i++) { // put a number in each element of the cats array cats[i] = i; } return 0; // return a zero to signify success } 

为什么会这样

请注意,您从未必须在此处调用malloc,因为在[main]函数内部声明了cat [3]。 main中的局部变量只有在程序退出时才会被销毁。 除非程序非常简单,否则malloc将用于创建和控制数据结构的生命周期。

另请注意,rainCats是硬编码返回0. rainCats中没有任何内容会使其失败,例如尝试访问文件,网络请求或其他内存分配。 更复杂的程序有很多失败的原因,因此返回成功代码通常是有充分理由的。

因为int cat[3] = {0,0,0}; 声明一个只在调用函数时才存在的自动变量。

对于inited自动char数组,C中有一个特殊的“免除”,因此可以返回引用的字符串,但它不会推广到其他数组类型。

cat []在您正在调用的函数的堆栈上分配,当释放该堆栈时释放内存(当函数返回堆栈时应该被视为已释放)。

如果你想要做的是在调用帧中填充一个int数组,在指向你从调用帧控制的指针中传递;

 void somefunction() { int cats[3]; findMyCats(cats); } void findMyCats(int *cats) { cats[0] = 0; cats[1] = 0; cats[2] = 0; } 

当然这是设计的,我已经硬编码了数组长度为3,但这是从被调用的函数中获取数据所需要做的。

单个值有效,因为它被复制回调用帧;

 int findACat() { int cat = 3; return cat; } 

findACat 3中将findACat复制到调用框架,因为它是编译器可以为您完成的已知数量。 指针指向的数据无法复制,因为编译器不知道要复制多少。

当您定义像’cat’这样的变量时,编译器会为其分配一个地址。 名称和地址之间的关联仅在定义范围内有效。 在自动变量的情况下,范围是从定义开始的函数体。

自动变量在堆栈上分配。 堆栈上的相同地址在不同时间与不同变量相关联。 返回数组时,实际返回的是数组的第一个元素的地址。 不幸的是,在返回之后,编译器可以并且将重用该存储以实现完全不相关的目的。 你在源代码级别看到的是你的返回变量,没有明显的原因神秘地改变。

现在,如果你真的必须返回一个初始化数组,你可以将该数组声明为static。 静态变量具有永久性而非临时存储分配。 您需要记住,连续调用该函数将使用相同的内存,因此在进行下一次调用之前,可能需要将之前调用的结果复制到其他位置。

另一种方法是将数组作为参数传递并在函数中写入。 然后,调用函数拥有该变量,并且不会出现堆栈变量的问题。

除非你仔细研究堆栈是如何工作的,否则这些都没有多大意义。 祝好运。

不能返回一个数组。 你正在返回一个指针 。 这不是一回事。

您可以返回指向malloc()分配的内存的指针,因为malloc()已分配内存并保留它以供程序使用,直到您明确使用free()取消分配它。

您可能不会返回指向本地数组分配的内存的指针,因为只要该函数结束,本地数组就不再存在。

这是对象生存期的问题 – 不是作用域或堆栈或堆。 虽然这些术语与对象的生命周期有关,但它们并不等同于生命周期,而且您返回的对象的生命周期非常重要。 例如,动态分配的对象的生命周期从分配扩展到deallocataion。 当变量的范围结束时,局部变量的生命周期可能会结束,但如果它是静态的,它的生命周期就不会结束。

使用malloc()分配的对象的生命周期直到使用free()函数释放该对象。 因此,当您使用malloc()创建对象时,只要您没有释放它,您就可以合法地返回指向该对象的指针 – 当函数结束时它仍然是活动的。 事实上,你应该注意用指针做一些事情,以便在某处记住它,否则会导致泄漏。

当变量的范围结束时,自动变量的生命周期结束(因此范围与生命周期有关)。 因此,从函数返回指向此类对象的指针是没有意义的 – 一旦函数返回,指针就会无效。

现在,如果您的局部变量是static而不是自动的,那么它的生命周期超出了它的范围(因此范围不等于生命周期)。 因此,如果函数具有本地静态变量,则即使函数已返回,该对象仍将处于活动状态,并且从函数返回指向静态数组的指针是合法的。 虽然这会引入一组全新的问题,因为该对象只有一个实例,因此从函数中多次返回它会导致共享数据的问题(它基本上只有在初始化后数据没有改变时才有效)什么时候可以改变的明确规则)。

从另一个答案中得到的另一个例子是关于字符串文字 – 指向它们的指针可以从函数返回,而不是因为作用域规则,而是因为规则说字符串文字的生命周期延长到程序结束。