依赖于C编译器和编译器标志的版本,可以在函数的任何位置初始化变量(据我所知)。
我过去常常将所有变量放在函数的顶部,但讨论开始时关于变量的内存使用,如果在函数的任何其他位置定义的话。
下面我写了两个简短的例子,我想知道是否有人能解释我(或验证)如何分配内存。
示例1:变量y是在可能的返回语句之后定义的,因此我可能不会因为这个原因而使用此变量,因为我知道这并不重要如果变量放在函数的顶部,代码(内存分配)将是相同的。这是对的吗?
示例2:变量x在循环中初始化,这意味着此变量的范围仅在此循环内,但该变量的内存使用情况如何?如果放在功能的顶部会有什么不同吗?或者只是在函数调用时在堆栈上初始化?
编辑:总结一个主要问题: 减少变量的范围或改变第一次使用的位置(所以在其他地方而不是顶部)对内存使用有影响吗?
代码示例1
static void Function(void){
uint8_t x = 0;
//code changing x
if(x == 2)
{
return;
}
uint8_t y = 0;
//more code changing y
}
代码示例2
static void LoopFunction(void){
uint8_t i = 0;
for(i =0; i < 100; i ++)
{
uint8_t x = i;
// do some calculations
uartTxLine("%d", x);
}
//more code
}
答案 0 :(得分:6)
我习惯将所有变量放在函数顶部
过去在旧版本的C中需要这样,但是现代编译器放弃了这个要求。只要他们在首次使用时知道变量的类型,编译器就会获得所需的所有信息。
我想知道是否有人能解释我如何分配内存。
编译器决定如何在自动存储区域中分配内存。实现不仅限于为每个变量声明一个单独位置的方法。允许它们重用超出范围的变量位置,以及在某个点之后不再使用的变量。
在第一个示例中,变量y
被允许使用以前由变量x
占用的空间,因为y
的第一个使用点是在最后一个使用点之后x
。
在第二个示例中,循环中x
使用的空间可以重用于您在// more code
区域中声明的其他变量。
答案 1 :(得分:1)
基本上,故事是这样的。在原始汇编程序中调用函数时,可以自定义在进入函数时将函数使用的所有内容存储在堆栈中,并在离开时将其清理干净。某些CPU和ABI可能具有调用约定,其涉及自动堆叠参数。
可能因为这个原因,C和许多其他旧语言要求必须在函数的顶部(或在范围顶部)声明所有变量,以便{ }
反映push / pop在堆栈上。
在80年代/ 90年代左右,编译器开始有效地优化这些代码,因为它们只会在首次使用时为本地变量分配空间,并在没有进一步使用的情况下取消分配它。它。无论声明的变量在哪里 - 它对优化编译器都没有关系。
大约在同一时间,C ++解除了C所拥有的变量声明限制,并允许变量在任何地方声明。但是,C实际上并没有在1999年之前用更新的C99标准解决这个问题。在现代C中,您可以在任何地方声明变量。
因此,除非您使用的是一个令人难以置信的古老编译器,否则您的两个示例之间绝对没有性能差异。然而,尽可能地缩小变量的范围被认为是良好的编程习惯 - 尽管它不应该以牺牲可读性为代价来完成。
虽然这只是一种风格问题,但我个人更喜欢写这样的功能:
(请注意,您使用的是uint8_t错误的printf格式说明符)
#include <inttypes.h>
static void LoopFunction (void)
{
for(uint8_t i=0; i < 100; i++)
{
uint8_t x = i;
// do some calculations
uartTxLine("%" PRIu8, x);
}
//more code
}
答案 2 :(得分:0)
旧C只允许在块顶部声明(和初始化)变量。您可以在块内的任何位置初始化新块(一对{
和}
字符),这样您就可以使用它们在代码旁边声明变量:
... /* inside a block */
{ int x = 3;
/* use x */
} /* x is not adressabel past this point */
您可以在switch
语句,if
语句以及while
和do
语句(您可以在哪里初始化新块)中执行此操作
现在,您可以在允许语句的任何地方声明变量,并且该变量的范围从声明点到您已声明它的内部嵌套块的末尾。
编译器决定何时为局部变量分配存储,因此,您可以在创建堆栈帧时分配所有这些(这是gcc方式,因为它只分配局部变量一次)或者当您输入定义块时(例如,Microsoft C就是这样做的)在运行时分配空间需要在运行时推进堆栈指针,因此如果每堆栈帧只执行一次,则可以节省cpu周期(但浪费内存位置)。这里重要的是你不允许引用其范围定义之外的变量位置,所以如果你尝试这样做,你将得到未定义的行为。我发现了一个很长时间在互联网上运行的旧bug,因为没有人花时间使用Microsoft-C编译器(在核心转储中失败)编译该程序,而不是通常使用GCC编译它。代码使用在内部作用域中定义的局部变量(if语句的then
部分)通过引用在代码的其他部分(因为所有内容都在main
函数上,堆栈框架是一直呈现)Microsoft-C只是在退出if
语句时重新分配空间,但是GCC等到main
完成之后才这样做。只需在变量声明中添加static
修饰符(使其成为全局)就可以解决这个问题,并且不再需要进行重构。
int main()
{
struct bla_bla *pointer_to_x;
...
if (something) {
struct bla_bla x;
...
pointer_to_x = &x;
}
/* x does not exist (but it did in gcc) */
do_something_to_bla_bla(pointer_to_x); /* wrong, x doesn't exist */
} /* main */
更改为:
int main()
{
struct bla_bla *pointer_to_x;
...
if (something) {
static struct bla_bla x; /* now global ---even if scoped */
...
pointer_to_x = &x;
}
/* x is not visible, but exists, so pointer_to_x continues to be valid */
do_something_to_bla_bla(pointer_to_x); /* correct now */
} /* main */