如何解决从C中的函数返回数组
我写了一个返回数组的函数,虽然我知道我应该返回一个动态分配的指针,但我仍然想知道当我返回一个在函数内部声明的数组时会发生什么(没有将它声明为静态),当我注意到函数中内部数组的内存没有被释放时,我感到很惊讶,我让我的数组回到了主数组。 主要内容:
int main()
{
int* arr_p;
arr_p = demo(10);
return 0;
}
和函数:
int* demo(int i)
{
int arr[10] = { 0 };
for (int i = 0; i < 10; i++)
{
arr[i] = i;
}
return arr;
}
当我取消引用 arr_p
时,我可以看到 demo
函数中设置的 0-9 整数。
两个问题:
- 为什么我检查
arr_p
时发现它的地址与arr
函数中的demo
相同? - 为什么
demo_p
指向demo
中已经没有释放的数据(0-9 数字)?我预计arr
内的demo
会在我们离开demo
范围时被释放。
解决方法
在编程时必须注意的一件事是注意规则所说的内容,而不仅仅是看起来有效的内容。规则说你不应该返回一个指向本地分配数组的指针,这是一个真实的规则。
如果您在编写返回指向本地分配数组的指针的程序时没有收到错误消息,这并不意味着没有问题。 (虽然,这意味着您确实应该获得更新的编译器,因为任何体面的现代编译器都会对此发出警告。)
如果您编写的程序返回一个指向本地分配数组的指针并且它似乎可以工作,那也不意味着它没问题。对此要非常小心:一般来说,在编程中,尤其是在 C 中,看起来有效并证明您的程序没问题。您真正想要的是让您的程序出于正确的原因而运行。
假设您租了一套公寓。假设,当您的租约到期并且您搬出时,您的房东没有从您那里取回您的钥匙,但也没有更换锁。假设,几天后,你意识到你在一个壁橱的后面忘记了一些东西。假设你不经询问就偷偷溜回去试图收集它。接下来会发生什么?
- 碰巧,您的钥匙在锁中仍然有效。这是完全出乎意料,还是有点出乎意料,还是保证有效?
- 碰巧的是,您遗忘的物品仍然在壁橱中。它尚未清除。这是完全出乎意料,还是有点出乎意料,还是肯定会发生?
- 最后,无论是您的老房东还是警察,都没有因为您的这种非法侵入行为而与您搭讪。再说一次,这是完全出乎意料,还是有点出乎意料,还是几乎完全在意料之中?
您需要知道的是,在 C 中,重用您不再允许使用的内存几乎完全类似于潜入您不再租用的公寓。它可能有效,也可能无效。你的东西可能还在那里,也可能不在。你可能会遇到麻烦,也可能不会。无法预测会发生什么,也无法从发生或不发生的任何事情中得出(有效)结论。
回到你的程序:像 arr
这样的局部变量通常存储在调用堆栈中,这意味着即使在函数返回之后它们仍然存在,并且可能不会被覆盖直到下一个函数被调用并且将堆栈上的该区域用于其自身目的(甚至可能不会)。因此,如果您返回一个指向本地分配内存的指针,并立即取消引用该指针(在调用任何其他函数之前),它至少有可能“工作”。这再次类似于公寓的情况:如果还没有其他人搬进来,您遗忘的物品很可能还在那里。但这显然不是您可以依赖的东西。
arr
是 demo
中的一个局部变量,当你从函数返回时它会被销毁。由于您返回一个指向该变量的指针,因此该指针被称为 dangling。取消引用指针会使您的程序具有未定义行为。
修复它的一种方法是malloc
(内存分配)您需要的内存。
示例:
#include <stdio.h>
#include <stdlib.h>
int* demo(int n) {
int* arr = malloc(sizeof(*arr) * n); // allocate
for (int i = 0; i < n; i++) {
arr[i] = i;
}
return arr;
}
int main() {
int* arr_p;
arr_p = demo(10);
printf("%d\n",arr_p[9]);
free(arr_p) // free the allocated memory
}
输出:
9
为什么 demo_p
指向 demo
中已经没有释放的数据(0-9 数字)?我预计 arr
中的 demo
会在我们离开 demo
范围时被释放。
arr
对象的生命周期已经结束,读取 arr
之前占用的内存地址会使您的程序出现未定义行为。您可能会看到旧数据,或者程序可能会崩溃——或者做一些完全不同的事情。 任何事情都可能发生。
…我注意到我函数中内部数组的内存没有被释放…
内存释放不是你可以注意到或观察到的,除非通过查看记录内存预留的数据(在这种情况下,堆栈指针)。当内存被保留或释放时,这只是一个关于哪些内存可用或不可用的簿记过程。释放内存不一定会擦除内存或立即将其重新用于其他目的。看内存不一定能告诉你它是否在使用中。
当 int arr[10] = { 0 };
出现在函数内部时,它定义了一个数组,该数组在函数开始执行时自动分配(或者如果定义在某个嵌套范围内,则在函数执行内的某些时间)。这通常通过调整堆栈指针来完成。在普通系统中,程序有一个称为堆栈的内存区域,堆栈指针包含一个地址,该地址标记当前保留供使用的堆栈部分的末尾。当函数开始执行时,堆栈指针会发生变化,以便为该函数的数据保留更多内存。当函数执行结束时,堆栈指针会改变以释放该内存。
如果您保留指向该内存的指针(如何做到这一点是另一回事,将在下面讨论),您将不会在函数返回后立即“注意到”或“观察”该内存的任何变化。这就是为什么您看到 arr_p
的值是 arr
拥有的地址的原因,也是您看到该内存中旧数据的原因。
如果调用其他函数,栈指针会为新函数调整,该函数一般会使用内存为自己的目的,然后该内存的内容会发生变化。您在 arr
中的数据将消失。初学者遇到的一个常见例子是:
int main(void)
{
int *p = demo(10);
// p points to where arr started,and arr’s data is still there.
printf("arr[3] = %d.\n",p[3]);
// To execute this call,the program loads data from p[3]. Since it has
// not changed,3 is loaded. This is passed to printf.
// Then printf prints “arr[3] = 3.\n”. In doing this,it uses memory
// on the stack. This changes the data in the memory that p points to.
printf("arr[3] = %d.\n",p[3]);
// When we try the same call again,the program loads data from p[3],// but it has been changed,so something different is printed. Two
// different things are printed by the same printf statement even
// though there is no visible code changing p[3].
}
回到如何获得指向内存的指针的副本,编译器遵循 C 标准中抽象指定的规则。 C 标准在 arr
中定义了数组 demo
的抽象生命周期,并说生命周期在函数返回时结束。它进一步说,当指针指向的对象的生命周期结束时,指针的值变得不确定。
如果您的编译器简单地生成代码,就像您使用带有 -O0
的 GCC 编译以关闭优化时那样,它通常会将地址保留在 p
中,您将看到上述行为。但是,如果您打开优化并编译更复杂的程序,编译器会尝试优化它生成的代码。它不是机械地生成汇编代码,而是尝试找到执行程序定义行为的“最佳”代码。如果您使用具有不确定值的指针或尝试访问生命周期已结束的对象,则您的程序没有定义的行为,因此编译器的优化可能会产生新程序员无法预料的结果。
亲爱的,如您所知,在局部函数中声明的变量的存在仅在该局部作用域内。完成所需的任务后,函数将终止,然后销毁局部变量。当你试图从 demo() 函数返回一个指针时,但是一旦我们从 demo() 出来,指针指向的数组就会被销毁。因此,您确实正在尝试返回一个指向取消分配内存的悬空指针。但是我们的规则建议我们不惜一切代价避免悬空指针。
所以你可以通过在使用 free() 释放内存后重新初始化来避免它。您也可以使用 malloc() 分配一些连续的内存块,或者您可以将 demo() 中的数组声明为静态数组。当本地函数成功退出时,这也会存储分配的内存常量。
谢谢亲爱的..
#include<stdio.h>
#define N 10
int demo();
int main()
{
int* arr_p;
arr_p = demo();
printf("%d\n",*(arr_p+3));
}
int* demo()
{
static int arr[N];
for(i=0;i<N;i++)
{
arr[i] = i;
}
return arr;
}
OUTPUT : 3
或者你也可以写成......
#include <stdio.h>
#include <stdlib.h>
#define N 10
int* demo() {
int* arr = (int*)malloc(sizeof(arr) * N);
for(int i = 0; i < N; i++)
{
arr[i]=i;
}
return arr;
}
int main()
{
int* arr_p;
arr_p = demo();
printf("%d\n",*(arr_p+3));
free(arr_p);
return 0;
}
OUTPUT : 3
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。