【问题标题】:Multiple definition of const variables at header file头文件中 const 变量的多重定义
【发布时间】:2014-08-04 15:27:29
【问题描述】:

我在 flag.h 中定义了一些常量,以便 link.clinkedlist.h 可以使用它。 但是当我编译时:

clang -Wall main.c link.c linkedlist.c

我得到以下内容

/tmp/linkedlist-o2mcAI.o:(.rodata+0x0): `VARIABLE_NAME'的多重定义

/tmp/link-oXhyfE.o:(.rodata+0x0): 这里先定义

对于 link.clinkedlist.c 中使用的 flag.h 中的所有变量,最后:

clang:错误:链接器命令失败,退出代码为 1(使用 -v 查看调用)


ma​​in.cflag.hlink.hlink.c的示例代码、linkedlist.hlinkedlist.c

main.c

#include <stdio.h>
#include "linkedlist.h"

int main(void) {
    LinkedList* list = LinkedList_new();
}

flag.h

#ifndef FLAG_H_
#define FLAG_H_

#include <limits.h>

#define FALSE 0
#define TRUE 1

const int OK = 1;
const int ERROR = -1;
const int FLAG = 0;

// other constants

#endif

link.h

#ifndef LINK_H_
#define LINK_H_

typedef struct Link {
    int value;
    struct Link* next;
} Link;

Link* Link_new(int value);

int useExample(int value);

// other methods

#endif

link.c

#include <stdio.h>
#include <stdlib.h>

#include "link.h"
#include "flag.h"

Link* Link_new(int value)
{
    Link* link = malloc(sizeof(Link));
    link->value = value;
    link->next = NULL;
    return link;
}

useExample(int value)
{
    if (value == 0) {
        return OK; // defined in flag.h
    } else {
        return FLAG; // defined in flag.h
    }
}

// other methods

linkedlist.h

#ifndef LINKEDLIST_H_
#define LINKEDLIST_H_

#include "link.h"

typedef struct LinkedList {
    Link* first;
    unsigned int size;
} LinkedList;

LinkedList* LinkedList_new();

int anotherUseExample(int value);

// other methods

#endif

linkedlist.c

#include <stdio.h>
#include <stdlib.h>

#include "linkedlist.h"
#include "flag.h"

LinkedList* LinkedList_new() {
    LinkedList* list = malloc(sizeof(LinkedList));
    list->first = NULL;
    list->size = 0;
    return list;
}

anotherUseExample(int value)
{
    if (value == 0) {
        return FLAG; // defined in flag.h
    } else {
        return ERROR; // defined in flag.h
    }
}

// other methods

那么如何在 link.clinkedlist.c 中使用 flag.h 而不会出现多重定义?

还有... 我编码头文件和编译的方式正确吗?


-v 的完整输出:

clang version 3.3 (tags/RELEASE_33/rc3)
Target: i386-redhat-linux-gnu
Thread model: posix
 "/usr/bin/clang" -cc1 -triple i386-redhat-linux-gnu -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -main-file-name main.c -mrelocation-model static -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -fuse-init-array -target-cpu pentium4 -target-linker-version 2.23.52.0.1 -v -resource-dir /usr/bin/../lib/clang/3.3 -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/clang/3.3/include -internal-externc-isystem /usr/include -internal-externc-isystem /usr/lib/gcc/i686-redhat-linux/4.8.1/include -Wall -fdebug-compilation-dir /home/jharvard/Desktop/Code/LinkedList -ferror-limit 19 -fmessage-length 80 -mstackrealign -fobjc-runtime=gcc -fobjc-default-synthesize-properties -fdiagnostics-show-option -fcolor-diagnostics -backend-option -vectorize-loops -o /tmp/main-JmZTmN.o -x c main.c
clang -cc1 version 3.3 based upon LLVM 3.3 default target i386-redhat-linux-gnu
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /usr/bin/../lib/clang/3.3/include
 /usr/include
 /usr/lib/gcc/i686-redhat-linux/4.8.1/include
End of search list.
 "/usr/bin/clang" -cc1 -triple i386-redhat-linux-gnu -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -main-file-name link.c -mrelocation-model static -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -fuse-init-array -target-cpu pentium4 -target-linker-version 2.23.52.0.1 -v -resource-dir /usr/bin/../lib/clang/3.3 -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/clang/3.3/include -internal-externc-isystem /usr/include -internal-externc-isystem /usr/lib/gcc/i686-redhat-linux/4.8.1/include -Wall -fdebug-compilation-dir /home/jharvard/Desktop/Code/LinkedList -ferror-limit 19 -fmessage-length 80 -mstackrealign -fobjc-runtime=gcc -fobjc-default-synthesize-properties -fdiagnostics-show-option -fcolor-diagnostics -backend-option -vectorize-loops -o /tmp/link-FtygcZ.o -x c link.c
clang -cc1 version 3.3 based upon LLVM 3.3 default target i386-redhat-linux-gnu
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /usr/bin/../lib/clang/3.3/include
 /usr/include
 /usr/lib/gcc/i686-redhat-linux/4.8.1/include
End of search list.
 "/usr/bin/clang" -cc1 -triple i386-redhat-linux-gnu -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -main-file-name linkedlist.c -mrelocation-model static -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -fuse-init-array -target-cpu pentium4 -target-linker-version 2.23.52.0.1 -v -resource-dir /usr/bin/../lib/clang/3.3 -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/clang/3.3/include -internal-externc-isystem /usr/include -internal-externc-isystem /usr/lib/gcc/i686-redhat-linux/4.8.1/include -Wall -fdebug-compilation-dir /home/jharvard/Desktop/Code/LinkedList -ferror-limit 19 -fmessage-length 80 -mstackrealign -fobjc-runtime=gcc -fobjc-default-synthesize-properties -fdiagnostics-show-option -fcolor-diagnostics -backend-option -vectorize-loops -o /tmp/linkedlist-n0zF1a.o -x c linkedlist.c
clang -cc1 version 3.3 based upon LLVM 3.3 default target i386-redhat-linux-gnu
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /usr/bin/../lib/clang/3.3/include
 /usr/include
 /usr/lib/gcc/i686-redhat-linux/4.8.1/include
End of search list.
 "/usr/bin/ld" --eh-frame-hdr -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o a.out /usr/lib/gcc/i686-redhat-linux/4.8.1/../../../crt1.o /usr/lib/gcc/i686-redhat-linux/4.8.1/../../../crti.o /usr/lib/gcc/i686-redhat-linux/4.8.1/crtbegin.o -L/usr/lib/gcc/i686-redhat-linux/4.8.1 -L/usr/lib/gcc/i686-redhat-linux/4.8.1/../../.. -L/lib -L/usr/lib /tmp/main-JmZTmN.o /tmp/link-FtygcZ.o /tmp/linkedlist-n0zF1a.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i686-redhat-linux/4.8.1/crtend.o /usr/lib/gcc/i686-redhat-linux/4.8.1/../../../crtn.o
/tmp/linkedlist-n0zF1a.o:(.rodata+0x4): multiple definition of `ERROR_indexOutOfBounds'
/tmp/link-FtygcZ.o:(.rodata+0x4): first defined here
/tmp/linkedlist-n0zF1a.o:(.rodata+0x8): multiple definition of `ERROR_invalidArgument'
/tmp/link-FtygcZ.o:(.rodata+0x8): first defined here
/tmp/linkedlist-n0zF1a.o:(.rodata+0x10): multiple definition of `FLAG_notFound'
/tmp/link-FtygcZ.o:(.rodata+0x10): first defined here
/tmp/linkedlist-n0zF1a.o:(.rodata+0xc): multiple definition of `FLAG_undefined'
/tmp/link-FtygcZ.o:(.rodata+0xc): first defined here
/tmp/linkedlist-n0zF1a.o:(.rodata+0x0): multiple definition of `OK'
/tmp/link-FtygcZ.o:(.rodata+0x0): first defined here
clang: error: linker command failed with exit code 1 (use -v to see invocation)

【问题讨论】:

  • (a) 将 static 添加到它们的声明中,或 (b) extern 它们,删除初始化,并将定义 with 初始化移动到单个翻译单元,或 (c) 使它们成为enum 的一部分。你有选择。

标签: c compiler-errors linker header-files


【解决方案1】:

C 中的#include 指令只是从头文件中复制文本。这意味着当您同时编译link.clinkedlist.c 时,来自flag.h 的常量定义被复制到两者中,并且所有这些常量现在都在link.o 和linkedlist.o 中定义。当你链接程序时,你会得到一个名字,你会得到一个多重定义错误。

您需要将声明与定义分开,并创建一个 flag.c 文件,在其中定义 const int OK = 1; 等,在 flag.h 处您将保留 const int OK; 等。这样,常量将被编译成单个目标文件 - flag.o - 链接时不会出现多重定义错误。

【讨论】:

  • 虽然这是一个很好的解释,但这是包建议。 const 变量只是完成任务的错误工具。请看我的回答。
【解决方案2】:

由于您不需要将这些常量作为对象,因此您应该使用不同的功能来定义它们,即枚举。

enum { OK = 1, ERROR = -1, FLAG = 0, ONE = 1, };

这些是int 类型,永远不会导致多个符号错误。它们的优点是它们可以保留在 .h 文件中。因此,所有使用它们的功能都可以看到价值,并且可以更好地进行优化。

从上面的例子可以看出,值不必按顺序出现,同一个值可能出现多次。

【讨论】:

  • 这可以使用,但仅限于非常具体的标准。首先,使用 const 允许任何类型,并在编译中保留类型检查。枚举仅限于 int 类型,这可能并不总是需要的。另外应该注意的是你不能有任何重叠的值,所以如果你将它用于一些通用配置项,你可能会在更改一些参数时遇到麻烦,然后它们与另一个参数的数值相同.
  • @MattG,没有什么禁止常量具有相同的值。
【解决方案3】:

标志.h:

extern const int OK, ERROR, FLAG;

flag.c:

const int OK = 1;
const int ERROR = -1;
const int FLAG = 0;

【讨论】:

    【解决方案4】:

    @Idan Arye 和@Jens Gustedt 提供了两种解决方案。两者都有优点和缺点。 枚举的一个主要优点是我们不需要为元素分配任何值,编译器会自动分配值。重复条目的可能性较小,我们可以安全地添加或删除新元素。

    枚举的缺点是它默认是int。如果我们在嵌入式编程等内存受限的系统中需要 uint8_t,我们可能会遇到问题。但是一些编译器支持 -fshort-enums 编译器选项,它允许编译器将枚举类型的大小设置为可以容纳所有枚举值的最小数据类型。但我们必须专门设置这些选项。

    在 C 中也不能前向声明枚举类型的变量

    在头文件和源文件中使用 const 时,我们需要再次将所有 const 复制到这两个文件中。

    值也可能重复(如果值必须是唯一的,则很危险)。

    const 的另一个主要缺点是它们不能用于初始化静态或全局变量。它们不被视为编译时常量,而是被视为只读变量。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-13
      • 2016-12-19
      • 1970-01-01
      • 2012-01-16
      • 2018-12-10
      相关资源
      最近更新 更多