C++ 在函数顶部或单独的作用域中声明变量?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3773396/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 13:41:01  来源:igfitidea点击:

Declare variables at top of function or in separate scopes?

c++cfunctionstack

提问by Kaije

Which is preferred, method 1 or method 2?

首选方法 1 还是方法 2?

Method 1:

方法一:

LRESULT CALLBACK wpMainWindow(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    switch (msg)
    {
        case WM_PAINT:
        {
            HDC hdc;
            PAINTSTRUCT ps;

            RECT rc;
            GetClientRect(hwnd, &rc);           

            hdc = BeginPaint(hwnd, &ps);
            // drawing here
            EndPaint(hwnd, &ps);
            break;
        }
        default: 
            return DefWindowProc(hwnd, msg, wparam, lparam);
    }
    return 0;
}

Method 2:

方法二:

LRESULT CALLBACK wpMainWindow(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    RECT rc;

    switch (msg)
    {
        case WM_PAINT:
            GetClientRect(hwnd, &rc);

            hdc = BeginPaint(hwnd, &ps);
            // drawing here
            EndPaint(hwnd, &ps);
            break;

        default: 
            return DefWindowProc(hwnd, msg, wparam, lparam);
    }
    return 0;
}

In method 1, if msg = WM_PAINT when wpMainWindow function is called, does it allocate memory for all the variables on the stack at the beginning? or only when it enters the WM_PAINT scope?

方法一中,如果调用wpMainWindow函数时msg = WM_PAINT,是不是一开始就为栈上的所有变量分配内存?还是只有当它进入 WM_PAINT 范围时?

Would method 1 only use the memory when the message is WM_PAINT, and method 2 would use the memory no matter what msg equaled?

方法 1 是否仅在消息为 WM_PAINT 时使用内存,而方法 2 会使用内存,无论 msg 等于什么?

回答by AnT

Variables should be declared as locally as possible.

变量应尽可能在本地声明。

Declaring variables "at the top of the function" is always a disastrously bad practice. Even in C89/90 language, where variables can only be declared at the beginning of the block, it is better to declare them as locally as possible, i.e. at the beginning of smallest local block that covers the desired lifetime of the variable. Sometimes it might even make sense to introduce a "redundant" local block with the only purpose of "localizing" the variable declaration.

在“函数顶部”声明变量总是一种灾难性的糟糕做法。即使在 C89/90 语言中,变量只能在块的开头声明,最好尽可能在本地声明它们,即在覆盖变量所需生命周期的最小本地块的开头。有时甚至可以引入一个“冗余”局部块,其唯一目的是“本地化”变量声明。

In C++ and C99, where it is possible to declare variable anywhere in the code, the answer is pretty straightforward: again, declare each variable as locally as possible, and as close as possible to the point where you use it the very first time. The primary rationale for that is that in most cases this will allow you to supply a meaningful initializer to the variable at the point of declaration (instead of declaring it without initializer or with a dummy initializer).

在 C++ 和 C99 中,可以在代码的任何位置声明变量,答案非常简单:同样,尽可能在本地声明每个变量,并尽可能接近您第一次使用它的地方。这样做的主要理由是,在大多数情况下,这将允许您在声明点为变量提供有意义的初始化程序(而不是在没有初始化程序或使用虚拟初始化程序的情况下声明它)。

As for the memory usage, in general a typical implementation will immediately (as you enter the function) allocate the maximum space required for all variables that exist at the same time. However, your declaration habits might affect the exact size of that space. For example, in this code

至于内存使用,通常典型的实现会立即(在您进入函数时)为同时存在的所有变量分配所需的最大空间。但是,您的声明习惯可能会影响该空间的确切大小。例如,在这段代码中

void foo() {
  int a, b, c;

  if (...) {
  }

  if (...) {
  }
}

all three variables exist at the same time and generally the space for all three has to be allocated. But in this code

所有三个变量同时存在,通常必须为所有三个变量分配空间。但是在这段代码中

void foo() {
  int a;

  if (...) {
    int b;
  }

  if (...) {
    int c;
  }
}

only two variables exist at any given moment, meaning that space for only two variables will be allocated by a typical implementation (band cwill share the same space). This is another reason to declare variables as locally as possible.

在任何给定时刻只有两个变量存在,这意味着只有两个变量的空间将由典型的实现分配(b并且c将共享相同的空间)。这是尽可能在本地声明变量的另一个原因。

回答by paxdiablo

Whether something's allocated on the stack in case 1 is implementation defined. Implementations aren't even requiredto have a stack.

在情况 1 中是否在堆栈上分配了一些东西是实现定义的。实现甚至不需要有一个堆栈。

It's usually no slowerto do so since the operation tends to be a simple subtraction (for a downward growing stack) of one value from the stack pointer for the entire local variable area.

这样做通常不会更慢,因为操作往往是从整个局部变量区域的堆栈指针中减去一个值(对于向下增长的堆栈)。

The thing that's important here is that the scope should be as local as possible. In other words, declare your variables as late as possible and only keep them around as long as needed.

这里重要的是范围应该尽可能本地化。换句话说,尽可能晚地声明你的变量,并只在需要时保留它们。

Note that declaring here is at a different abstraction level to allocating space for them. The actual space may be allocated at the start of the function (implementation level) but you can only use those variables while they're scoped (C level).

请注意,此处的声明与为其分配空间处于不同的抽象级别。实际空间可能在函数开始时分配(实现级别),但您只能在限定范围内(C 级别)时使用这些变量。

Locality of information is important, just like its cousin, encapsulation.

信息的局部性很重要,就像它的表亲一样,封装。

回答by Ben Voigt

I like Method 3:

我喜欢方法3:

LRESULT wpMainWindowPaint(HWND hwnd)
{
    HDC hdc;
    PAINTSTRUCT ps;

    RECT rc;
    GetClientRect(hwnd, &rc);           

    hdc = BeginPaint(hwnd, &ps);
    // drawing here
    EndPaint(hwnd, &ps);
    return 0;
}

LRESULT CALLBACK wpMainWindow(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    switch (msg)
    {
        case WM_PAINT:      return wpMainWindowPaint(hwnd);
        default:            return DefWindowProc(hwnd, msg, wparam, lparam);
    }
}

If it deserves its own scope for organization purposes, it deserves its own function. If you're worried about function call overhead, make it inline.

如果它应该为组织目的而拥有自己的范围,那么它就应该拥有自己的功能。如果您担心函数调用开销,请使其内联。

回答by xtofl

Since it's the compiler's job to optimize my code, and an hour of compiler-time is way cheaper than an hour of my time, and my time gets wasted if I need to scroll up and down the code to see where a variable was declared, I think my company wants me to keep everything as local as possible.

因为优化我的代码是编译器的工作,而且一个小时的编译时间比我一个小时的时间要便宜得多,如果我需要上下滚动代码以查看变量的声明位置,我的时间就会被浪费,我认为我的公司希望我尽可能地保持本地化。

Not even am I talking about 'the smallest block', but 'as near to the place where it is used'!

我什至不是在谈论“最小的街区”,而是“靠近使用它的地方”!

LRESULT CALLBACK wpMainWindow(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) 
{ 
    switch (msg) 
    { 
        case WM_PAINT: 
        { 
            RECT rc; 
            GetClientRect(hwnd, &rc);            

            { // sometimes I even create an arbitrary block 
              // to show correlated statements.
              // as a side-effect, the compiler may not need to allocate space for 
              // variables declared here...
              PAINTSTRUCT ps; 
              HDC hdc = BeginPaint(hwnd, &ps); 
              // drawing here 
              EndPaint(hwnd, &ps); 
            }
            break; 
        } 
        default:  
            return DefWindowProc(hwnd, msg, wparam, lparam); 
    } 
    return 0; 
} 

回答by Steve Townsend

Define the variables in the narrowest scope where they are relevant. There's no reason to use Method 2 above in my opinion.

在相关的最窄范围内定义变量。在我看来,没有理由使用上面的方法 2。

Stack space is only likely to be used when the variables are in scope. As @paxdiablo points out, your locals may wind up in registers rather than on the stack, if the compiler can find the space for them.

仅当变量在范围内时才可能使用堆栈空间。正如@paxdiablo 指出的那样,如果编译器可以为它们找到空间,那么您的本地人可能会在寄存器中而不是在堆栈中。

回答by David Thornley

Memory allocation is not specified in the Standard to this detail, so for a real answer you'll have to specify compiler and platform. It's not going to matter for performance.

标准中没有为此细节指定内存分配,因此要获得真正的答案,您必须指定编译器和平台。这对性能无关紧要。

What you want is readability, and in general that's done by declaring variables in the smallest usable scope, and preferably when you can immediately initialize them with reasonable values. The smaller a variable's scope, the less it can potentially interact with the rest of the program in unpredictable ways. The closer the declaration to initialization, the less opportunity for anything bad to happen.

您想要的是可读性,通常是通过在最小的可用范围内声明变量来完成的,最好是当您可以立即用合理的值初始化它们时。变量的范围越小,它以不可预测的方式与程序的其余部分进行交互的可能性就越小。声明越接近初始化,任何不好的事情发生的机会就越少。

What would probably be better is something like

可能会更好的是

RECT rc;
GetClientRect(hwnd, &rc);
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

This is for C++. For C, the rule is similar, except that earlier versions of C required all the variables to be declared at the top of a block.

这是针对 C++ 的。对于 C,规则是类似的,只是早期版本的 C 要求在块的顶部声明所有变量。

回答by Jens Gustedt

You can't know at what point the stack reservation is done.

您无法知道堆栈预留是在什么时候完成的。

For readability I would go with C99 (or C++). That allows you the declaration of a variable really there where first use it.

为了可读性,我会使用 C99(或 C++)。这允许您在第一次使用它的地方真正声明一个变量。

 HDC hdc = BeginPaint(hwnd, &ps);

回答by Jingguo Yao

For Java programming language, the common practice is to declare local variables only when needed in a method.

对于 Java 编程语言,通常的做法是仅在方法中需要时才声明局部变量。

void foo(int i) {
  if (i == 1)
    return;
  Map map1 = new HashMap();
  if (i == 2)
    return;
  Map map2 = new HashMap();
}

For C++ programming language, I also suggest the same practice since declaring variables with a non-trivial constructor involves execution cost. Putting all these declarations at the method beginning causes unneeded cost if some of these variables will be used.

对于 C++ 编程语言,我也建议采用相同的做法,因为使用非平凡构造函数声明变量涉及执行成本。如果将使用其中一些变量,将所有这些声明放在方法开头会导致不必要的成本。

void foo(int i) 
{
  if (i == 1)
    return;
  std::map<int, int> map1; // constructor is executed here
  if (i == 2)
    return;
  std::map<int, int> map2; // constructor is executed here
}

For C, the story is different. It depends on architecture and compiler. For x86 and GCC, putting all the declarations at function beginning and declaring variables only when needed have the same performance. The reason is that C variables don't have a constructor. And the effect on stack memory allocation by these two approaches is the same. Here is an example:

对于 C,情况就不同了。这取决于体系结构和编译器。对于 x86 和 GCC,将所有声明放在函数开头和仅在需要时才声明变量具有相同的性能。原因是 C 变量没有构造函数。并且这两种方法对堆栈内存分配的影响是相同的。下面是一个例子:

void foo(int i)
{
  int m[50];
  int n[50];
  switch (i) {
    case 0:
      break;
    case 1:
      break;
    default:
      break;
  }
}

void bar(int i) 
{
  int m[50];
  switch (i) {
    case 0:
      break;
    case 1:
      break;
    default:
      break;
  }
  int n[50];
}

For both functions, the assembly code for stack manipulation is:

对于这两个函数,堆栈操作的汇编代码是:

pushl   %ebp
movl    %esp, %ebp
subl    0, %esp

Putting all the declarations at function beginning is common in Linux kernel code.

将所有声明放在函数开头在 Linux 内核代码中很常见。

回答by myeviltacos

There's no need to pollute the stack with variables that are possibly never used. Allocate your vars right before they are used. Overlooking the RECT rcand the subsequent call to GetClientRect, Ben Voight's method is the way to go.

没有必要用可能从未使用过的变量污染堆栈。在使用之前分配你的变量。忽略RECT rc和随后对 的调用GetClientRect,Ben Voight 的方法是要走的路。