Declare variables at top of function or in separat

2020-01-27 12:31发布

Which is preferred, method 1 or method 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?

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?

9条回答
神经病院院长
2楼-- · 2020-01-27 13:11

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

It's usually no slower to 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).

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

查看更多
forever°为你锁心
3楼-- · 2020-01-27 13:11

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

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.

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:

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    $400, %esp

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

查看更多
该账号已被封号
4楼-- · 2020-01-27 13:17

I like Method 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.

查看更多
萌系小妹纸
5楼-- · 2020-01-27 13:22

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.

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).

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 (b and c will share the same space). This is another reason to declare variables as locally as possible.

查看更多
我命由我不由天
6楼-- · 2020-01-27 13:23

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

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.

查看更多
一纸荒年 Trace。
7楼-- · 2020-01-27 13:24

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 rc and the subsequent call to GetClientRect, Ben Voight's method is the way to go.

查看更多
登录 后发表回答