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;
请注意,pHead
和 pCurrent
在list.h
或list.c
中没有原型化或体现
现在检查问题代码中的main.h
和main.c
:
/*----main.h file-----*/
#ifndef MAIN_H
#define MAIN_H
#include<stdio.h>
#include "list.h"
#endif
单独来看,main.h 需要stdio.h
和list.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++中的“已经定义”错误的主要内容,如果未能解决你的问题,请参考以下文章