我遇到这种情况:
{
float foo[10];
for (int i = 0; i < 10; i++) {
foo[i] = 1.0f;
}
object.function1(foo); // stores the float pointer to a const void* member of object
}
object.function2(); // uses the stored void pointer
第二个函数调用中浮点指针的内容是否未知?当我运行程序时,似乎得到了奇怪的结果。但是如果我将float foo [10]声明为const并在声明中初始化它,我会得到正确的结果。为什么会这样?
答案 0 :(得分:8)
对于第一个问题,一旦超出范围,使用foo是不正确的。我不确定它是否在规范中定义了行为,但这样做肯定是不正确的。最好的情况是你的程序会立即崩溃。
至于第二个问题,为什么让它成为常量?这是一个实现工件。可能发生的事情是数据被写入DLL的数据部分,因此在程序的生命周期内有效。相反,原始样本将数据放在堆栈中,它的生命周期要短得多。代码仍然是错误的,它恰好起作用。
答案 1 :(得分:2)
是的,当你调用function2时,foo []超出了范围。它是一个自动变量,存储在堆栈中。当代码退出它定义的块时,它被解除分配。你可能已经在别处存储了一个引用(指针),但这没有意义。
答案 2 :(得分:1)
在这两种情况下,您都会获得未定义的行为。任何事都可能发生。
您正在存储指向本地声明的数组的指针,但是一旦包含数组定义的作用域退出该数组 - 并且其所有成员都将被销毁。
现在存储的指针不再指向浮点数,甚至不能指向可用于浮点数的有效内存地址。它可能是一个重用于其他内容的地址,或者它可能会继续包含原始数据不变。无论哪种方式,尝试取消引用指针仍然无效,无论是读取还是写入浮点值。
答案 3 :(得分:1)
对于这样的声明:
{
type_1 variable_name_1;
type_2 variable_name_2;
type_3 variable_name_3;
}
声明,变量在堆栈上分配。
您可以打印出每个变量的地址: printf(“%p \ n”,variable_name) 并且您将看到地址大致增加(但并不总是完全等于),每个变量需要存储其数据的空间量。 当达到'}'并且变量超出范围时,堆栈变量使用的内存将被回收。只需从一个名为“堆栈指针”的特殊指针中减去一些数字就可以很有效地完成这一操作,该指针指示新堆栈变量的数据将在何处分配数据。通过递增和递减堆栈指针,程序有一个极其快速的计算方法,因为变量的内存将存活。它是如此重要的概念,每个主要处理器都只为堆栈指针维护一段特殊的内存。
数组的内存也会从程序的数据堆栈中推出并弹出,而数组指针是指向程序堆栈内存的指针。虽然语言规范说访问范围外变量所拥有的数据具有不确定的后果,但结果通常很容易预测。 通常,您的数组指针将继续保持其原始数据,直到分配了新的堆栈变量和分配的数据(即内存被重用于其他目的)。
所以不要这样做。复制数组。
我不清楚标准对常量数组的说法(可能是同样的事情 - 当原始声明超出范围时内存无效)。但是,您的不同行为是可解释的如果您的编译器为程序启动时初始化的常量分配了一块内存,之后,foo会在进入作用域时指向该数据。至少,如果 I 正在编写编译器,那可能就是我所做的,因为它的速度非常快,并导致使用最少量的内存。这个理论很容易通过以下方式测试:
void f()
{
const float foo[2] = {99, 101};
fprintf( "-- %f\n", foo[0] );
const_cast<foo*>(foo)[0] = 666;
}
两次调用foo()。如果在调用之间更改了打印值(或者抛出了无效的内存访问异常),那么可以肯定的是,foo的数据被分配在特殊区域中,用于上述代码所写的常量。
在特殊区域中分配内存不适用于非常量数据,因为递归函数可能会导致堆栈中同时存在许多单独的变量副本,每个副本可能包含不同的数据。
答案 4 :(得分:0)
这两种情况都是未定义的行为。当控制离开块时,您应该考虑释放基于堆栈的变量。
答案 5 :(得分:0)
与foo相关的内存超出范围并被回收。 在{}之外,指针无效。
让对象管理自己的内存而不是引用外部指针是一个好主意。在这种特定情况下,您的对象可以在内部分配自己的foo并将数据复制到其中。然而,这实际上取决于你想要达到的目标。
答案 6 :(得分:0)
目前发生的事情你可能只是设置一个指针(看不到代码,所以我不能确定)。该指针将指向对象foo
,该对象在该点的范围内。但是当它超出范围时,所有地狱都会崩溃,而C标准一旦超出范围就无法保证数据会发生什么。它可以被任何东西覆盖。它适用于const
数组,因为你很幸运。不要那样做。
如果您希望代码按原样正常工作,function1()
将需要将数据复制到对象成员中。这意味着你还必须知道数组的长度,这意味着你必须传递它或者有一些很好的终止方法。
答案 7 :(得分:0)
对于像这样的简单问题,最好给出一个简单的答案,而不是关于堆栈和内存地址的3段。
有两对大括号{}
,一对在另一对。数组是在第一个左括号之后声明的{所以它在最后一个支撑之前停止了}
结束
在回答问题时,无论您自己对问题的理解程度如何,您都必须在提问者的级别回答问题,否则您可能会使学生感到困惑。
- 经验丰富的ESL老师