如果我在if语句中定义一个数组,那么在编译期间会分配内存,例如。
if(1)
{
int a[1000];
}
else
{
float b[1000];
}
然后为浮点数的2 * 1000
内存+ 4 * 1000
的内存被分配?
答案 0 :(得分:5)
它在运行时保留在堆栈上(假设一个非平凡的条件 - 在您的情况下,编译器将只排除else
部分)。这意味着它只存在于范围块内({}
)之间。
答案 1 :(得分:1)
在您的示例中,只有int的内存在堆栈上分配(1000 * sizeof(int))。
你可以猜到,这是在运行时发生的。生成的代码具有在输入相应的代码块时在堆栈上分配空间的指令。
请记住,由于语言的语义,这种情况正在发生。块结构引入了一个新的作用域,在该作用域中分配的任何自动变量的生命周期都与作用域一样长。在C中,这是通过在堆栈上分配它来实现的,当范围消失时它会崩溃。
只是为了推动这一点,请注意,如果变量具有不同的性质,则分配会有所不同。
if(1)
{
static int a[1000];
}
else
{
static float b[1000];
}
在这种情况下,为int和浮点分配空间。这些变量的生命周期是程序。但是可见性在它们被分配的块范围内。
答案 2 :(得分:1)
范围
在一对{ }
范围内声明的变量在堆栈中。这适用于在函数开头或函数内的任何{ }
对中声明的变量。
int myfunc()
{
int i = 0; // On the stack, scoped: myfunc
printf("%i\n");
if (1)
{
int j = 1; // On the stack, scope: this if statement
printf("%i %i\n",i,j);
}
printf("%i %i\n",i,j); // Won't work, no j
}
目前,变量的范围仅限于周围的{ }
。我记得一些较旧的Microsoft编译器没有限制范围,并且在上面的示例中,最终printf()
将编译。
那么它在记忆中的位置是什么?
i
和j
的内存仅保留在堆栈中。这与使用malloc()
完成的内存分配不同。这很重要,因为相比之下调用malloc()
非常慢。此外,使用malloc()
动态分配内存时,您必须调用free()
。
实际上,编译器提前知道函数变量需要什么空间,并且将生成相对于调用myfunc()
时堆栈指针所指向的内存的代码。只要堆栈足够大(通常为2MB,取决于操作系统),一切都很好。
在调用myfunc()
且堆栈指针已经接近堆栈末尾的情况下发生堆栈溢出(即myfunc()
由一个函数调用,而函数又由另一个函数调用它自己被另一个调用,等等。每一层嵌套的函数调用都会将堆栈指针移动一点,并且只有在函数返回时才会移回)。
如果堆栈指针和堆栈末尾之间的空间不足以容纳myfunc()
中声明的所有变量,myfunc()
的代码将只是尝试使用位置超出堆栈的末尾。这几乎总是一件坏事,而且注意到出现问题的确切程度有多糟糕和难度取决于操作系统。在小型嵌入式微控制器上,它可能是一场噩梦,因为它通常意味着程序数据的其他部分(例如全局变量)被静默覆盖,并且调试起来非常困难。在更大的系统(Linux,Windows)上,操作系统会告诉你发生了什么,或者只会使堆栈更大。
运行时效率注意事项
在上面的示例中,我将值分配给i
和j
。这实际上占用了少量的运行时。只有在将if语句和后续分支评估到声明j
的位置后,才会为j
分配1。
比如说if语句没有被评估为true;在这种情况下j
永远不会被分配1.如果在j
的开头声明了myfunc()
,那么无论if语句是否为真,它总是被赋值为1 - a很少浪费时间。但是考虑一个不太重要的例子,其中一个大数组被声明为初始化;这需要更多的执行时间。
int myfunc()
{
int i = 0; // On the stack, scoped: myfunc
int k[10000] = {0} // On the stack, scoped: myfunc. A complete waste of time
// when the if statement evaluates to false.
printf("%i\n");
if (0)
{
int j = 1; // On the stack, scope: this if statement
// It would be better to move the declaration of k to here
// so that it is initialised only when the if evaluates to true.
printf("%i %i %i\n",i,j,k[500]);
}
printf("%i %i\n",i,j); // Won't work, no j
}
将k
的声明放在myfunc()
的顶部意味着每次调用myfunc()
时都会执行一个循环10,000长的初始化k。但它永远不会被使用,因此循环完全是浪费时间。
当然,在这些简单的例子中,编译器会优化不必要的代码等。在实际代码中,编译器无法提前预测执行流程将会是什么,然后事情就会存在。
答案 3 :(得分:0)
作为 DCoder & paddy 纠正了我,内存将在编译时计算,但在运行时分配在堆栈内存段中,但是使用范围&定义数组的块的生命周期。分配的内存大小取决于int
&的大小。您系统中的float
。阅读this for an overview on C memory map
答案 4 :(得分:0)
if
块中数组的内存将在运行时在堆栈上分配。 else
部分将由编译器优化(删除)。有关变量将在何处分配内存的更多信息,请参阅Segmentation Fault when writing to a string