链接文件时无法解决多个定义错误 [重复]

Unable to resolve multiple definition error while linking files [duplicate]

提问人:ErrorEliminator 提问时间:6/24/2023 最后编辑:Vlad from MoscowErrorEliminator 更新时间:6/24/2023 访问量:137

问:

我正在研究本学期计算机编程课程中的一个问题,但无法解决我面临的链接器错误。有人可以看看以下文件并让我知道出了什么问题。按照指示,我放置了守卫以确保不会发生多个定义,但守卫似乎没有像我预期的那样运行,因为 gcc 给了我错误,说 count 和 booksCatalog 被多次定义。

以下是用于该问题的 C 文件和头文件。

book.h的

#ifndef BOOK_H
#define BOOK_H

#include <stdio.h>

typedef enum shelf{Shelf1=1,Shelf2=2,Shelf3=3,Shelf4=4} SHELF;

typedef struct book{
    int ID;    
    SHELF shelfNum;
    float price;
} BOOK;


// the function should create a new variable of the type struct book (typdefed as BOOK)
// using the parameters passed to it and return it.
BOOK newBook(int ID, SHELF shelfNum, float price);

// this function should print the values of the members of book1 (passed as parameter)
void printBook(BOOK book1);

#endif

book.c(图书.c)

#include "book.h"

BOOK newBook(int ID, SHELF shelfNum, float price)
{
    // implement this function as per specification in books_def.h
    BOOK newbook = {ID, shelfNum, price};
    return newbook;
}

void printBook(BOOK book1)
{
   // implement this function as per specification in books_def.h
   printf("Book ID : %d\n", book1.ID);
   printf("Book shelf number : %d\n", book1.shelfNum);
   printf("Price of Book: %f\n\n",book1.price);
   return;
}

目录.h

#ifndef CATALOG_H
#define CATALOG_H

#include "book.h"

#define MAX_SIZE 100

BOOK booksCatalog[MAX_SIZE];
int count;

// This function should take an input parameter of the type struct book (typdefed as BOOK)
// and it to the booksCatalog array at position count. Then, count should be incremented.
void addBookToCatalog(BOOK book1);


// This function should print all the books that are added to booksCatalog array.
// It should use the printBook() function defined in "book_def.h" to print the details of each book.
void printBookCatalog();


// This function should sort the booksCatalog array based on the ID of the book.
void sortBookCatalogOnID();

#endif

catalog.c网站

#include "catalog.h"

void addBookToCatalog(BOOK book1)
{
    // implement this function as per specification in books_catalog.h
    if (count<MAX_SIZE)
    {
        booksCatalog[count]=book1;
        count++;
    }
    else
        printf("Catalog is full!");
    return;
    
}

void printBookCatalog()
{
    // implement this function as per specification in books_catalog.h
    for (int i=0; i<count; i++)
    {
        printBook(booksCatalog[i]);
    }
    return;
}

void sortBookCatalogOnID()
{
    // implement this function as per specification in books_catalog.h
    int min;
    for (int i=0; i<count-1; i++)
    {
        min=i;
        for (int j=i+1; j<count; j++)
        {
            if ((booksCatalog[j].ID)<(booksCatalog[min].ID))
                min=j;
        }
        BOOK temp = booksCatalog[i];
        booksCatalog[i]=booksCatalog[min];
        booksCatalog[min]=temp;
    }
    return;
}

main.c

#include "catalog.h"
#include "book.h"

int main()
{
    count = 0;
    BOOK book1 = newBook(1847, Shelf3, 8768.95);
    BOOK book2 = newBook(5984, Shelf1, 7845.25);
    BOOK book3 = newBook(6325, Shelf2, 3154.47);
    BOOK book4 = newBook(5843, Shelf2, 1487.51);
    BOOK book5 = newBook(7894, Shelf2, 541.29);
    // printBook(book1); 

    // adding book1 to catalog of books
    addBookToCatalog(book1);
    addBookToCatalog(book2);
    addBookToCatalog(book3);
    addBookToCatalog(book4);
    addBookToCatalog(book5);

    printBookCatalog();

    sortBookCatalogOnID();

    printBookCatalog();

    return 0;
}
c gcc linker-errors 声明 定义

评论

2赞 Andreas Wenzel 6/24/2023
相关/可能的重复:如何使用 extern 在源文件之间共享变量?

答:

1赞 Vlad from Moscow 6/24/2023 #1

标头有两个变量的暂定定义catalog.h

BOOK booksCatalog[MAX_SIZE];
int count;

由于标头包含在模块中,因此实际上这两个变量在每个翻译单元中定义两次,例如catalog.cmain.c

BOOK booksCatalog[MAX_SIZE] = { 0 };
int count = { 0 };

来自 C23 标准(6.9.2 外部对象定义)

2 具有文件范围的对象的标识符声明 没有初始值设定项,也没有存储类说明符,或者 使用存储类说明符 static,构成暂定 定义。如果翻译单元包含一个或多个暂定 标识符的定义,并且翻译单元不包含 该标识符的外部定义,则行为为 就像翻译单元包含文件范围声明一样 的标识符,其复合类型截至 转换单元,初始值设定项等于 {0}

您应该在标头中声明不带定义定义的变量catalog.h

extern BOOK booksCatalog[MAX_SIZE];
extern int count;

(注意:这些声明中的变量不应初始化。否则,它们在标头中外部定义,链接器将再次发出错误,因为标头包含在两个转换单元中。

并在模块中定义它们,例如catalog.c

BOOK booksCatalog[MAX_SIZE] = { 0 };
int count = 0;

注意的是,在 C 中,与 C++ 相反,要使文件(C++ 中的命名空间)作用域变量的外部定义,您需要初始化它。在 C++ 中,没有“暂定定义”这样的概念。

1赞 Andreas Wenzel 6/24/2023 #2

在你的头文件中,你有一行catalog.h

int count;

这是一个暂定的定义。在这种情况下,这个暂定定义将始终成为实际定义。

这意味着两个源文件,每个文件都提供了变量的定义。这违反了单一定义规则,即使两个定义相同。main.ccatalog.ccount

为了遵守单一定义规则,只有一个源文件应提供定义。因此,我建议在头文件中更改行catalog.h

int count;

自:

extern int count;

这样一来,这一行将是一个声明,而不是一个暂定的定义。

但是,现在变量在 和 中声明,但未在任何地方定义。因此,为了遵守单一定义规则,还必须在其中一个源文件中定义变量。此定义可以是(暂定)定义countmain.ccatalog.c

int count;

或明确的定义:

int count = 0;

变量也有同样的问题。booksCatalog

评论

0赞 ErrorEliminator 6/24/2023
但是,难道不应该让守卫阻止这种情况发生吗?如果宏已经定义好了,那么定义就不应该编译了吧?我刚开始用 C 语言编程,并不知道暂定定义到底发生了什么。
0赞 Andreas Wenzel 6/24/2023
@ErrorEliminator:包含保护可防止同一头文件多次包含在同一个源文件(翻译单元)中。它不会阻止头文件包含在多个源文件中。
0赞 ErrorEliminator 6/24/2023
如果头文件包含在多个源文件中,则当它们被链接时,所定义的宏不会触发 #ifndef 指令并跳过暂定定义的编译。P.S. 很多事情都超出了我的脑海,所以如果我公然错了,你能不能简化一下你的意思,因为我不知道你的答案中使用了很多内容,比如翻译单位和暂定定义
0赞 Andreas Wenzel 6/24/2023
@ErrorEliminator:每个翻译单元(即 源文件)至少在概念上是独立编译的。之后,将编译过程发出的目标代码链接起来,形成可执行文件。宏仅适用于编译过程,因此仅影响单个翻译单元的编译。当链接发生时,这些宏不再存在。因此,预处理器指令只会对当前翻译单元产生影响,不会对其他翻译单元产生任何影响。.c#define#define BOOK_H
0赞 ErrorEliminator 6/24/2023
哦,所以定义仍然会在两个文件中发生,因为 main.c 和 catalog.c 是独立编译的。宏只阻止在 main.c 中多次包含 book.h,因为它是单独包含的,也是通过在 main.c 中包含 catalog.h 和在 catalog.h 中包含 book.h 间接包含的,所以给出 extern 关键字只是告诉编译器我的变量在这里声明,但在其他地方定义?