在函数顶部或单独的范围内声明变量?

Kai*_*ije 36 c c++ stack function

哪个是优选的,方法1或方法2?

方法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;
}
Run Code Online (Sandbox Code Playgroud)

方法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;
}
Run Code Online (Sandbox Code Playgroud)

在方法1中,如果在调用wpMainWindow函数时msg = WM_PAINT,它是否在开始时为堆栈上的所有变量分配内存?或者只有当它进入WM_PAINT范围时?

方法1只在消息是WM_PAINT时才使用内存,而方法2是否会使用内存而不管msg等于什么?

AnT*_*AnT 63

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

在函数顶部声明变量总是一种灾难性的不良做法.即使在C89/90语言中,变量只能在块的开头声明,最好将它们声明为尽可能本地,即在覆盖变量所需寿命的最小局部块的开头.有时甚至可能引入一个"冗余"的本地块,其唯一目的是"本地化"变量声明.

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

至于内存使用情况,通常一个典型的实现将立即(当你输入函数时)分配同时存在的所有变量所需的最大空间.但是,您的声明习惯可能会影响该空间的确切大小.例如,在此代码中

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

  if (...) {
  }

  if (...) {
  }
}
Run Code Online (Sandbox Code Playgroud)

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

void foo() {
  int a;

  if (...) {
    int b;
  }

  if (...) {
    int c;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

  • @JeremyP:我把它称之为灾难性的,因为我能想到的缺点比专业人士的名单长得多.其中一个缺点是,这种声明风格鼓励变量重用,导致真正灾难性的错误代码. (8认同)
  • @flies与大多数建议一样,接下来是一个未说明的内容:"除非你有充分的理由在你的情况下做其他事情".在某些情况下,效率可能足以成为将变量移出循环的原因.(虽然对于原始类型,它在大多数情况下可能没有多大区别.)并且您仍然可以围绕变量并与另一个范围循环以防止它们泄漏到其他任何地方,从而遵循原始想法的意图. (5认同)
  • 当您在循环内声明变量时,“尽可能在本地声明”与效率之间存在轻微冲突。我的倾向是在循环之前声明用于大循环的变量 - 这是不好的做法吗?(假设变量在循环之外没有用途/意义。) (2认同)
  • @flies如果语言是C++,那将取决于构造函数和operator =完成的工作量.声明和初始化调用[copy]构造函数(即使使用=符号进行初始化)并分配(在声明之外)调用operator =.因此,在循环外声明一个对象会更糟糕,因为它会创建一个将被覆盖的虚拟对象.OTOH如果你正在处理原语,ptrs,refs或plain C,就不必担心,因为声明本身几乎不产生代码:真正生成代码的是=符号之后的代码. (2认同)

pax*_*blo 12

在案例1中是否在堆栈上分配的内容是实现定义的.甚至不需要实现堆栈.

这样做通常不会,因为操作往往是来自整个局部变量区域的堆栈指针的一个值的简单减法(对于向下增长的堆栈).

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

请注意,此处的声明处于不同的抽象级别,以便为它们分配空间.实际空间可以在函数的开头(实现级别)分配,但是只能在它们作用域(C级)时使用这些变量.

信息的位置很重要,就像它的堂兄一样.


Ben*_*igt 7

我喜欢方法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);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果它应该有自己的组织范围,它应该有自己的功能.如果您担心函数调用开销,请将其设置为内联.


xto*_*ofl 5

由于优化代码是编译器的工作,而一个小时的编译时间比一个小时的时间便宜得多,而且如果我需要上下滚动代码以查看变量的声明位置,则会浪费我的时间,我认为我的公司希望我尽可能将所有内容都保留在本地。

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

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; 
} 
Run Code Online (Sandbox Code Playgroud)