C4533警告:为什么goto跳过变量初始化?

时间:2011-11-11 23:39:57

标签: c++

我得到了:

  

警告C4533:goto FreeDC跳过了'b'的初始化。

但如果代码到达FreeDC中的标签WM_CREATE,则“b”未初始化。如果在那种情况下没有初始化,它的初始化如何可以滑雪。我只是不明白警告。

#include <windows.h>

class A
{
    int i;

    public:
    A() {};
    A(int i) : i(i) {}
};

LRESULT CALLBACK WndProc(HWND, UINT, UINT, LONG);

HINSTANCE ghInstance;

/************************************************************************************************************************

    WinMain(hInstance, hPrevInstance, pszCmdLine, nCmdShow)

************************************************************************************************************************/

int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pszCmdLine, int nCmdShow)
{
    ghInstance = hInstance;

    WNDCLASSEX  wndclassx;

    wndclassx.cbSize        = sizeof(WNDCLASSEX);
    wndclassx.style         = CS_HREDRAW | CS_VREDRAW;
    wndclassx.lpfnWndProc   = WndProc;
    wndclassx.cbClsExtra    = 0;
    wndclassx.cbWndExtra    = 0;
    wndclassx.hInstance     = hInstance;
    wndclassx.hIcon         = NULL;
    wndclassx.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wndclassx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wndclassx.lpszMenuName  = NULL;
    wndclassx.lpszClassName = L"WndProc";
    wndclassx.hIconSm       = NULL;

    if( !RegisterClassEx(&wndclassx) ) return 0;

    HWND hWnd = CreateWindow(L"WndProc", L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
                             CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

    ShowWindow(hWnd, SW_SHOWMAXIMIZED);
    UpdateWindow(hWnd);

    MSG msg;

    while( GetMessage(&msg, NULL, 0, 0) )
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    //  Retorna msg.wParam

    return (int)msg.wParam;
}

/************************************************************************************************************************

    WndProc(hwnd, message, wParam, lParam)

************************************************************************************************************************/

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, UINT wParam, LONG lParam)

{
    static A a;
    static int i;

    switch ( message )
    {
        case WM_CREATE:
        {
            HDC hDC;
            if( !(hDC = GetDC(hwnd)) ) return -1;

            int iLogPixelsY = GetDeviceCaps(hDC, LOGPIXELSY);

            LOGFONT lf;
            memset(&lf, 0, sizeof(LOGFONT));
            lf.lfHeight = -MulDiv(11, iLogPixelsY, 72);
            wcscpy_s(lf.lfFaceName, LF_FACESIZE, L"Cambria Math");

            HFONT hFont;
            if( !(hFont = CreateFontIndirect(&lf)) ) goto FreeDC;

            hFont = (HFONT)SelectObject(hDC, hFont);

            int j = 5;
            i = j;

            A b(2);
            a = b;
            return 0;

            FreeDC: ReleaseDC(hwnd, hDC);
            return -1; 
        }
        break;

        case WM_DESTROY:

        PostQuitMessage(0);
        break;

        default:

        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}

5 个答案:

答案 0 :(得分:4)

如果你使用b

goto的构造函数将不会被调用,但它仍在范围内。这在技术上是一个错误,虽然有些编译器只发出警告。

以下是一个例子:

int main() {
  goto foo;
  int bar = 5;
  foo:
  ++bar; // doesn't work if goto is used - bar isn't initialized
}

看起来你似乎没有使用b,但它的析构函数仍然被调用:

int main() {
  goto foo;
  A b;
  foo:
  b.~A(); // compiler silently adds destructor and other cleanup here
          // won't work if goto is used - b isn't initialized
}

答案 1 :(得分:1)

老实说我不知道​​,但是当goto声明足够时你为什么要使用if

if( (hFont = CreateFontIndirect(&lf)) ) {
    hFont = (HFONT)SelectObject(hDC, hFont);

    int j = 5;
    i = j;

    A b;
    a = b(2);
    return 0;
}
else {
    FreeDC: ReleaseDC(hwnd, hDC);
    return -1; 
}

// break; here is unnecessary.

答案 2 :(得分:1)

您不能使用gotoswitch [*]跳过对象的初始化(对于用户定义的类型以及int s等基本类型,它都适用)。在您的情况下,您没有使用已跳过初始化的对象,因此最好的解决方案是通过限制b的范围使编译器清楚。

        if( !(hFont = CreateFontIndirect(&lf)) ) goto FreeDC;

        hFont = (HFONT)SelectObject(hDC, hFont);

        int j = 5;
        i = j;
    {
        A b;
        a = b(2);
        return 0;
    }
        FreeDC: ReleaseDC(hwnd, hDC);

[*]所以这些都是非法的:

switch(x) {
    case 1:
        int y=1;
    case 2:
        // y not initialized if x==2

if (x) goto l;
int y=1;
l: // y not initialized if x!=0

如果y是引用,常量或具有重要构造函数的用户定义对象,这一点尤为重要。

标准在6.7 / 3中表示:

  

可以转移到块中,但不能转移到块中   通过初始化绕过声明。一个跳跃的程序   从具有自动存储持续时间的局部变量的点开始   除非是在范围内,否则它的范围是不正确的   变量具有POD类型(3.9)并且在没有初始化程序的情况下声明   (8.5)。

答案 3 :(得分:1)

您可以通过引入goto跳过的合适的本地范围来避免此问题:

        HFONT hFont;

        if( !(hFont = CreateFontIndirect(&lf)) )
        {
          goto FreeDC;
        }

        hFont = (HFONT)SelectObject(hDC, hFont);

        {               // new scope; skipped entirely by goto
          int j = 5;
          i = j;

          A b;
          a = b(2);
        }

        return 0;

    FreeDC:
        ReleaseDC(hwnd, hDC);
        return -1;

如果你仔细考虑C ++和范围以及自动对象生命周期,你会得出结论:goto 真的会对整个编程模型造成严重破坏。这就是为什么有许多(通常是默默暗示的)条件,你可以去哪里,而不是。通常,如果范围包含新的自动变量,则跳转到范围的中间是有问题的。我们通过引入goto跳跃完全跳过的新的本地范围来避免这种情况。

答案 4 :(得分:1)

考虑一个较小的,微不足道的测试用例:

struct Object {
   Object(int i) : i(i) { }
   int i;
};

int main() {
    Object a(5);
    goto Label;
    Object b(6);
  Label:
    cout << a.i << " " << b.i << endl;
}

在最后一行,a.i显然是5。但是b.i的价值是多少?当创建该对象时,它应该初始化为6,但是您明确告诉程序跳过该行。它可以是任何东西。

现在,让我们假装Object是一种更有用的类型:

struct Object {
  Object(int i) : p(new int(i)) { }
  ~Object() { delete p; }
  //insert copy/move constructors/assignment here
  int* p;
};

int main() {
    Object a(5);
    goto Label;
    Object b(6);
  Label:
    cout << *a.p << endl;
}

现在,你实际上从未使用b.p,所以看起来你跳过初始化的事实并不重要。 a.p已正确初始化,因此输出5,没问题。但是你从main返回,并且析构函数开始被调用...包括调用b.~Object()的{​​{1}}。但是delete p;从未被初始化,所以谁知道那条线会做什么?

在这些情况下,我认为代码实际上是格式错误的,编译器需要拒绝它。似乎不是直接拒绝它,编译器会选择警告你可能存在的问题,以便你可以自己决定是否存在问题。