将全局变量添加到另一个文件中时的奇怪行为

Posted

技术标签:

【中文标题】将全局变量添加到另一个文件中时的奇怪行为【英文标题】:Weird behaviour when global variables are added in another file 【发布时间】:2018-04-28 06:25:07 【问题描述】:

这是我的简单程序:

#include "stdafx.h"
#include<Windows.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void initBackBuffer(HWND hwnd);

HDC hBackDC = NULL;
HBITMAP hBackBitmap = NULL;

const int WIDTH = 512;
const int HEIGHT = 512;

DWORD screenBuffer[WIDTH * HEIGHT];


void draw(HWND hwnd) 
    HDC hWinDC = GetDC(hwnd);

    SetBitmapBits(hBackBitmap, HEIGHT * WIDTH * sizeof(DWORD), (const void*)(screenBuffer));
    BitBlt(hWinDC, 0, 0, WIDTH, HEIGHT, hBackDC, 0, 0, SRCCOPY);
    ReleaseDC(hwnd, hWinDC);


int WINAPI wWinMain(HINSTANCE hInstace, HINSTANCE hPrevInstace, LPWSTR lpCmdLine, int nCmdShow) 
    memset(screenBuffer, 0, sizeof(screenBuffer));
    MSG msg =  0 ;
    WNDCLASS wnd =  0 ;

    wnd.lpfnWndProc = WndProc;
    wnd.hInstance = hInstace;
    wnd.lpszClassName = L"Window";

    if (!RegisterClass(&wnd)) 
        return 0;
    

    HWND hwnd = CreateWindowEx(NULL, wnd.lpszClassName, L"Window",
        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HEIGHT, NULL, NULL, hInstace, NULL);

    if (!hwnd) 
        return 0;
    

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    for (int i = 0; i <= 512; i++) 
        screenBuffer[i * WIDTH + 0] = 0x00FF0000;
    

    while (true) 
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
            if (msg.message == WM_QUIT) 
                break;
            

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        


        draw(hwnd);
    

    return msg.wParam;


LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

    switch (msg)
        case WM_CREATE:
            initBackBuffer(hwnd);
            break;
        case WM_DESTROY:
            DeleteDC(hBackDC);
            DeleteObject(hBackBitmap);
            PostQuitMessage(0);
            break;
    
    return DefWindowProc(hwnd, msg, wParam, lParam);


void initBackBuffer(HWND hwnd) 
    HDC hWinDC = GetDC(hwnd);

    hBackDC = CreateCompatibleDC(hWinDC);
    hBackBitmap = CreateCompatibleBitmap(hWinDC, WIDTH, HEIGHT); 
    SetBitmapBits(hBackBitmap, HEIGHT * WIDTH * sizeof(DWORD), (const void*)(screenBuffer));

    SelectObject(hBackDC, hBackBitmap);
    ReleaseDC(hwnd, hWinDC);

output 符合预期。

我搬家了

const int WIDTH = 512;
const int HEIGHT = 512;

DWORD screenBuffer[WIDTH * HEIGHT]; 

进入Global.h,我在主文件中添加了#include "Global.h"

主文件:

#include "stdafx.h"
#include<Windows.h>
#include "Global.h"

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void initBackBuffer(HWND hwnd);

HDC hBackDC = NULL;
HBITMAP hBackBitmap = NULL;


void draw(HWND hwnd) 
    HDC hWinDC = GetDC(hwnd);

    SetBitmapBits(hBackBitmap, HEIGHT * WIDTH * sizeof(DWORD), (const void*)(screenBuffer));
    BitBlt(hWinDC, 0, 0, WIDTH, HEIGHT, hBackDC, 0, 0, SRCCOPY);
    ReleaseDC(hwnd, hWinDC);


int WINAPI wWinMain(HINSTANCE hInstace, HINSTANCE hPrevInstace, LPWSTR lpCmdLine, int nCmdShow) 
    memset(screenBuffer, 0, sizeof(screenBuffer));
    MSG msg =  0 ;
    WNDCLASS wnd =  0 ;

    wnd.lpfnWndProc = WndProc;
    wnd.hInstance = hInstace;
    wnd.lpszClassName = L"Window";

    if (!RegisterClass(&wnd)) 
        return 0;
    

    HWND hwnd = CreateWindowEx(NULL, wnd.lpszClassName, L"Window",
        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HEIGHT, NULL, NULL, hInstace, NULL);

    if (!hwnd) 
        return 0;
    

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    for (int i = 0; i <= 512; i++) 
        screenBuffer[i * WIDTH + 0] = 0x00FF0000;
    

    while (true) 
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
            if (msg.message == WM_QUIT) 
                break;
            

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        


        draw(hwnd);
    

    return msg.wParam;


LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

    switch (msg)
        case WM_CREATE:
            initBackBuffer(hwnd);
            break;
        case WM_DESTROY:
            DeleteDC(hBackDC);
            DeleteObject(hBackBitmap);
            PostQuitMessage(0);
            break;
    
    return DefWindowProc(hwnd, msg, wParam, lParam);


void initBackBuffer(HWND hwnd) 
    HDC hWinDC = GetDC(hwnd);

    hBackDC = CreateCompatibleDC(hWinDC);
    hBackBitmap = CreateCompatibleBitmap(hWinDC, WIDTH, HEIGHT); 
    SetBitmapBits(hBackBitmap, HEIGHT * WIDTH * sizeof(DWORD), (const void*)(screenBuffer));

    SelectObject(hBackDC, hBackBitmap);
    ReleaseDC(hwnd, hWinDC);

Global.h

#pragma once


const int WIDTH = 512;
const int HEIGHT = 512;

DWORD screenBuffer[WIDTH * HEIGHT];

我得到一个错误的white window。

我不明白为什么会发生这种情况,因为编译器无论如何都会将Global.h 的内容复制到主文件中,因此两种变体都应该产生相同的结果。

这个问题的原因是什么?

【问题讨论】:

#include "stdafx.h" 放在标题中很危险,但我不知道这是否会导致您遇到的问题。 WM_PAINT 之外绘图是灾难的根源。你为什么要这么做? @MatteoItalia 你发现的话题似乎仍然是唯一的嫌疑人。您能否详细说明并将其转化为答案,也许是解决方案? @user4581301 是的,我纠正了这个错误。 我可能理解错了,但我以为您也将“definition-in-header”更改为“header-declare-code-define”。 【参考方案1】:

这里有个bug:

const int WIDTH = 512;
const int HEIGHT = 512;
DWORD screenBuffer[WIDTH * HEIGHT];
void foo()

    for (int i = 0; i <= 512; i++) 
        screenBuffer[i * WIDTH + 0] = 0x00FF0000;
    

这应该是i &lt; 512。否则它会覆盖一个随机内存位置,这可能会导致另一个位置出错,或者如果幸运的话不会出错。调试器可能会报告一个无意义的错误,或者根本没有错误。如果screenBuffer 是在堆栈上创建的,调试器可能会给出“堆损坏”错误。

考虑使用std::vector 以避免以后出现此问题。

vector<int> vec;
vec[vec.size()] = 0;//<- debug error

旁注:SetDIBitsToDeviceStretchDIBits 将直接设置位:

void draw(HWND hwnd) 

    BITMAPINFO bi;
    bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
    bi.bmiHeader.biBitCount = 32;
    bi.bmiHeader.biWidth = WIDTH;
    bi.bmiHeader.biHeight = HEIGHT;
    bi.bmiHeader.biPlanes = 1;
    bi.bmiHeader.biCompression = BI_RGB;
    HDC hdc = GetDC(hwnd);
    SetDIBitsToDevice(hdc, 0, 0, WIDTH, HEIGHT, 0, 0, 0, HEIGHT, screenBuffer,
        &bi, DIB_RGB_COLORS);
    ReleaseDC(hwnd, hdc);

【讨论】:

std::vector 默认情况下也未选中。 @MatteoItalia VS 调试器在遇到myvector[myvector.size()]时中断 这只是启用了“安全 STL”的 VC++ 对您的礼貌;超出向量边界是简单的 UB,不需要诊断(例如,使用 g++,您必须显式启用调试 STL 才能得到错误)。 @MatteoItalia VS 调试器没有“安全 STL”选项。这些错误检查在调试模式下默认启用。您想在优化代码中禁用冗余错误检查,因此这不是 100% 可靠的。但这是一个很大的进步。我对其他调试器不熟悉,我不知道他们为什么会默认禁用重要的错误检查。 调试器并没有真正参与,它是一种依赖于定义 (_ITERATOR_DEBUG_LEVEL) 的库编译模式,其默认值由编译模式控制。至少在某些较旧的 VC++ 版本中,即使在发布模式下也默认启用它,因为我记得我必须明确关闭它,因为它增加了 35% 的开销。 libstdc++ 等效项 (_GLIBCXX_DEBUG) 默认情况下未启用,因为要实现最广泛的检查(特别是在迭代器上),容器 ABI 会发生变化,这在链接其他静态库时会出现问题。

以上是关于将全局变量添加到另一个文件中时的奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章

提升局部变量掩盖全局变量?

c语言如何将数组作为全局变量

在java中,将局部变量作为参数传递到另一个类的方法里,在此方法里改变参数值,局部变量会改变吗

C 语言能不能在头文件定义全局变量?

Node.js:如何将全局变量传递到通过 require() 插入的文件中?

Cocoa xcode 4.3 全局变量