如何避免全局常量的“多重定义”错误?

Posted

技术标签:

【中文标题】如何避免全局常量的“多重定义”错误?【英文标题】:How to avoid "multiple definition" error for global constants? 【发布时间】:2014-07-12 20:34:12 【问题描述】:

我正在使用 Windows API 编写 C 程序。每个主要功能都有自己的文件,原型和包含等等都有一个标题:

// Headers & global constants
#pragma once
#define _WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <WindowsX.h>
#include <Windef.h>

#define szClassName TEXT("EthicsPresentationWnd")
// Prototypes
LRESULT CALLBACK WindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK FontProc1(HWND hWnd, LPARAM lParam);
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd);
ATOM RegisterWindow(HINSTANCE hInstance);

让我恼火的是#define szClassName 行。我不喜欢使用宏,并且希望有一个适当的全局变量wchar_t szClassName[],但如果我这样做,那么链接器会抱怨在每个包含标头的模块中定义了多个变量。

我认为#pragma once 指令会阻止这种情况,但它没有。

有没有办法解决这个问题?

【问题讨论】:

【参考方案1】:

解决方案是有一个单独的声明和定义...

标头(*.h;抱歉,我不知道 WinAPI 类型名称,根据需要进行调整):

extern const char szClassName[];

实现(*.c 或 *.cpp)

const char szClassName[] = "hello, world"

您看到了这个问题,因为每次您的 *.c 或 *.cpp 文件中的一个包含标题时都会声明一个新符号 szClassName(即使使用包含保护!);这会使链接器感到困惑(见下文)。

请注意,这将使sizeof(szClassName) 不再工作。

进一步说明:

经过预处理后,编译器基本上是这样看的:

文件“a.c”:const char someSymbol[] = &lt;some text, don't care what right now&gt;; 文件“b.c”:const char someSymbol[] = &lt;some text, don't care if it's the same&gt;; 文件“c.c”:const char someSymbol[] = &lt;some text, ditto&gt;;

当链接器链接目标文件(例如,“a.obj”、“b.obj”和“c.obj”)时,它会看到用新值定义了相同的符号(至少到目前为止与链接器有关)---因此它失败并出现错误。

【讨论】:

【参考方案2】:

放在中间

#ifndef GLOB_CONST_H
#define GLOB_CONST_H 

// All macro definitions
// and type definitions

#endif   

使用extern 关键字来声明你的全局变量并将这些声明放在这个头文件中。之后,您需要将所有变量的定义放在一个.c 文件中。

【讨论】:

头球后卫帮不了他。 #pragma once 负责标题保护;这是一个链接器,而不是编译器,错误(见我的回答)。【参考方案3】:

您可以将变量声明为static,以便包含 .h 文件的每个模块都获得其自己的本地唯一副本,链接器不会抱怨,因为每个副本都有本地链接而不是外部链接。这也消除了将变量声明为 extern 并将其定义在单独的 .c 文件中的需要。

static const TCHAR szClassName[] = TEXT("EthicsPresentationWnd");

或者;

static const TCHAR *szClassName = TEXT("EthicsPresentationWnd");

或者:

static LPCTSTR szClassName = TEXT("EthicsPresentationWnd");

【讨论】:

我认为这属于“除非你确定你知道自己在做什么,否则不要尝试”的类别。对我来说,拥有多个具有单独声明的变量副本的模块似乎是个坏主意。【参考方案4】:

在所有头文件中使用头文件保护,并在 .c 文件中声明一个全局变量,并在头文件中为该全局变量声明 extern。

#ifndef HEADER_FILE_NAME_H    /* if not defined already */
#define HEADER_FILE_NAME_H
extern wchar_t szClassName[];
#endif

在任何一个 .c 文件中定义全局变量。

wchar_t szClassName[];

【讨论】:

#pragma once 已经是一个标头守卫,所以这无济于事。看我的回答。

以上是关于如何避免全局常量的“多重定义”错误?的主要内容,如果未能解决你的问题,请参考以下文章

链接器如何解析多重定义的全局符号(强弱符号)------深入理解计算机系统

在多文件中C语言中全局变量的重定义

在多文件中C语言中全局变量的重定义

在多文件中C语言中全局变量的重定义

C++找到一个或多个多重定义的符号

编译C程序出现多重定义,怎么解决?