VC++中的“已经定义”错误

Posted

技术标签:

【中文标题】VC++中的“已经定义”错误【英文标题】:"already defined " error in VC++ 【发布时间】:2014-05-27 15:09:45 【问题描述】:

我正在学习以专业的方式编写程序。就像,通过创建单独的 .C 和 .h 文件。我决定编写一个带有结构的简单代码,但我遇到了错误。 我做过这些事情:

/*---list.h file-------*/
#ifndef LIST_H
#define LIST_H
struct list
    int a;
    struct list *next;
;
typedef struct list LIST;
LIST *pHead=NULL,*pCurrent=NULL;
void display(void);

#endif


/*---list.c file ---*/
#include "main.h"


    void display()
    
        pHead->a=100;
        printf("%d",pHead->a);
    


/*----main.h file-----*/

#ifndef MAIN_H
#define MAIN_H

#include<stdio.h>

#include "list.h"
#endif


/*---main.c file---*/
#include "main.h"


void main(void)

LIST *New=pHead;
display();
printf("\n\n%d",New->a);
getch();


当我编译代码时,出现以下错误 1>main.obj:错误 LNK2005:_pCurrent 已在 list.obj 中定义 1>main.obj : 错误 LNK2005: _pHead 已在 list.obj 中定义

谁能告诉我我做错了什么?我是否包含了两次,因为我收到了重新声明错误?

【问题讨论】:

那么pHead指向哪里? duplicate symbol error C++的可能重复 “我正在学习以专业的方式编写程序” 为什么不从int main( ) 开始呢? 【参考方案1】:

这是因为您在标题中定义事物,而不是仅仅声明它们。

这个:

LIST *pHead=NULL,*pCurrent=NULL;

表示每个包含列表头的 C 文件都尝试创建两个全局变量。然后,当您将这些 C 文件链接在一起时,这些变量会发生冲突。这是坏的,你不应该那样做。永远不要在标题中定义东西。

【讨论】:

我尝试删除 LIST *pHead=NULL,*pCurrent=NULL;来自 list.h 并在 list.c 中写了那行现在我收到更多错误【参考方案2】:

您在头文件中定义了对象,然后将它们包含在多个源文件中,从而破坏了one definition rule。

如果您想创建可以跨不同翻译单元使用的全局变量,您应该use the extern keyword。

【讨论】:

你能说得更详细些吗?关于我的代码。 @Priyanka:头文件中存在的任何内容都只会复制粘贴到包含标头的每个 translation unit。在 C/C++ 中,不允许对同一个命名符号进行多个定义(一个定义规则),所以在头文件中定义对象(pHead,pCurrent)会在多个源文件中包含头文件时创建多个定义。正确的方法是使用extern关键字(链接到 ans 详细解释了该部分)。希望这会有所帮助。 我更改了 LIST *pHead=NULL,*pCurrent=NULL;只列出 *pHead;LIST *pCurrent;错误消失了。 @Priyanka:因为通过更改,您现在只声明而不是定义 the objects.Read(***.com/questions/1410563/…) 但这并不能解决您的问题,因为您的要求是在不同的源文件中使用相同的对象,您的更改给您的是,每个翻译单元都有自己的对象副本,虽然对象的每个副本具有相同的名称,但它们并不相同,每个都是不同的。您需要使用extern 来实现这一点。请查看答案中的链接。 嗨,你的意思是我必须声明为 extern struct list e;用于在任何 .C 文件中制作对象?【参考方案3】:

一般来说,.c文件包含变量、函数等的体现;而.h 文件包含变量、函数等的原型,可以在它的伴侣.c 文件中找到。

一般情况下变量和函数体没有放在.h文件中; 只有变量和函数原型应该放在 .h 文件中

在考虑如何将代码拆分为单独的文件时,重要的是要考虑哪些函数、结构和宏是最原始的。例如,如果您编写两个函数,并且函数 'a' 调用函数 'b',则函数 'b' 是最原始的

这个想法是将函数分组到一个相关的“c”文件中,并且处于类似的原始级别。

在这个问题的情况下,更原始的列表函数应该体现在list.c中。然后 'list.h' 用于对其他 less 原语 .c 文件(例如 main.c)使用的函数和结构进行原型设计。

原始函数也是最自给自足的。虽然 较少原始 函数应该调用 较多原始 函数,但反过来会使代码流变得笨拙。

现在查看问题代码:

/*---list.c file ---*/
#include "main.h"

list.c 应该被认为比main.c更原始。因此,让list.c 包含main.h(专业上)不是一个好主意。 list.c更原始应该更自给自足。

与其包含main.h,不如让list.c 包含它自己的list.h,这样它就可以访问它自己的“结构列表”定义等。

void display()

    pHead->a=100;
    printf("%d",pHead->a);

为了更好地隔离list.c,上述函数不应引用“全局”变量(pHead)。相反,最好将“要显示的节点”作为参数传递给函数。

考虑到这一点,下面是如何改进“list.c”和“list.h”:

/*---list.h file-------*/
#ifndef LIST_H
 #define LIST_H

typedef struct NODE_S
   
   int a;
   struct list *next;
    NODE_T;

typedef struct LIST_S
   
   NODE_T *head;
    LIST_T;

extern void NodeDisplay(NODE_T *node);

#endif


/*---list.c file ---*/
#include <stdio.h> // printf()
#include "list.h"  // NODE_T, LIST_T

void NodeDisplay(NODE_T *node)
   
   printf("%d\n",pHead->a);
   return;
   

请注意,pHeadpCurrentlist.hlist.c 中没有原型化或体现


现在检查问题代码中的main.hmain.c

/*----main.h file-----*/

#ifndef MAIN_H
 #define MAIN_H

#include<stdio.h>

#include "list.h"
#endif

单独来看,main.h 需要stdio.hlist.h 的目的是什么?如果它们被删除,'main.h'中是否会有一些'未定义'?也许这两个包含文件并不真正属于main.h。 “但如果它们从 main.h 中删除,为什么还要有一个 main.h?”好点子。也许main.h 没有任何用途,甚至不应该存在。

main.c 文件是所有文件中最原始的文件,通常不应将任何内容导出到其他(更原始)文件。

/*---main.c file---*/
#include "main.h"

void main(void)
   
   LIST *New=pHead;
   display();
   printf("\n\n%d",New->a);
   getch();
   

那么main.c 究竟需要什么?它需要调用printf(),因此需要包含stdio.h。它调用display(),并引用LIST结构,所以它需要list.h

是的,那些 .h 文件包含在 main.h 中;好点子。但是,如果main.c 明确包含它需要的内容,则代码将不那么笨拙(更专业)。

考虑到这一理念,这里是重新设计的main.c,没有多余的main.h

/*---main.c file---*/
#include <stdio.h>   // printf()
#include <conio.h>   // getch()
#include "list.h"    // NodeDisplay(), LIST_T

int main(void)
   
   LIST_T pList = 
      
      .head = NULL
      ;

   /* Allocate & Insert a node into the list. */
   NodeCreate(&pList, 100);
   NodeDisplay(pList.head);
   getch();
   return(0);
   

这个版本的main.c 准确地包含了所需的内容,并且适当地调用了less primitive 函数。它不需要“全局变量”,因为它会根据需要将其本地存储传递给更原始的函数。

哦!你注意到函数NodeCreate()

虽然分配和插入新列表节点的操作可以在main.c 中执行,但这样的操作很可能很常见,与其他链表操作非常吻合。因此,将这样一个函数添加到list.c

/*---list.c file ---*/
#include <stdio.h>  // printf()
#include <stdlib.h> // malloc()
#include "list.h"   // NODE_T, LIST_T

void NodeDisplay(NODE_T *node)
   
   printf("%d\n",node->a);
   return;
   

void NodeCreate(LIST_T *list, int a)
   
   NODE_T *newNode = malloc(sizeof(*newNode));
   if(NULL == newNode)
      
      fprintf(stderr, "malloc(newNode) failed.\n");
      goto CLEANUP;
      

   if(NULL == list)
      
      fprintf(stderr, "Passing NULL as the list address not allowed.\n");
      goto CLEANUP;
      

   /* Initialize new node fields (payload) */
   newNode->a = a;

   /* Link newNode as new 'list head' node. */
   newNode->next = list->head ? list->head->next : NULL;
   list->head = newNode;     
   newNode=NULL;

  CLEANUP:

     if(newNode)
        free(newNode);

     return;
     

为了让这个函数可以从 less 原语 main.c 调用,将函数的原型添加到list.h

/*---list.h file-------*/
#ifndef LIST_H
 #define LIST_H

typedef struct NODE_S
   
   int a;
   struct list *next;
    NODE_T;

typedef struct LIST_S
   
   NODE_T *head;
   ;

extern void NodeDisplay(NODE_T *node);
extern void NodeCreate(LIST_T *list, int a);

#endif

见剧透代码here

【讨论】:

很好的解释,其实我现在明白了让.h对应一个.c文件的目的:)

以上是关于VC++中的“已经定义”错误的主要内容,如果未能解决你的问题,请参考以下文章

delphi调用vc编写的dll,出现错误

VC不能用c语言stdlib.h标准库中的宏是软件有问题吗

LNK2005:删除 VC++ 中已定义的错误

Release Debug 转载

为啥在定义全局名称时出现错误?

vc++中的链接错误