3_重复定义(MULTIPLE PUBLIC DEFINITIONS )
KEILC51编译ERRORL104MULTIPLEPUBLICDEFINITIONS重复定义

KEILC51编译ERROR L104: MULTIPLE
PUBLIC DEFINITIONS重复定义
c/c++语言中有很多地方要用到extern,但是如果
没有真正的了解它的意义,会给编程带来很大的麻烦,
为了使大家少走弯路,特祥细的说明一下。
对于比较小的程序,一般只有一个c文件和一个头
文件,全局变量我们通常会直接定义在c文件中,在程序之前加int i定义。
如果要在头文件中定义有以下两种方法:用extern来声明:extern int i;这一句只是对变量
i进行声明,在c文件的程序之前必须加上int i进行定义。
extern int i=0;这一句声明和定义都做了。
对于大一点的程序,有很多c文件和头文件,这个
时候全局变量就必须在头文件中声明(不需要初始化),
然后在一个c文件中定义(该初始化的要初始化)。
如果
在头文件中定义,则编译的时候会出现重复定义的错误。
如果只有头文件中声明就会出现没有定义有警告。
*** ERROR L104: MULTIPLE PUBLIC DEFINITIONS SYMBOL: K
MODULE: 222.obj (222)
出现上述错误则是因为变量k重复定义,把你的头文件中的变量定义前加extern(只是变量声明不用初始化),再在某一个你要调用该变量的c文件的程序之前再定义(注意第一个调用的c文件要负责附带初始化该变量,其他调用的c文件就不需要初始化过程啦)。
「keilc语言编程常见错误分析」

1.Warning 280:’i’:unreferencedlocal variable 说明局部变量i 在函数中未作任何的存取操作解决方法消除函数中i 变量的宣告及即定义的参数在程序中并未调用2Warning 206:’Music3’:missing function-prototype 说明Music3( )函数未作宣告或未作外部宣告所以无法给其他函数调用解决方法将叙述void Music3(void)写在程序的最前端作宣告如果是其他文件的函数则要写成extern voidMusic3(void),即作外部宣告3Error:318:can’t open file‘beep.h’说明在编译C:\8051\MANN.C程序过程中由于main.c 用了指令#i nclude “beep.h”,但却找不到所致解决方法编写一个beep.h的包含档并存入到c:\8051 的工作目录中ﻫ4 Error237:’LedOn’:function already has a body ﻫ说明LedOn()函数名称重复定义即有两个以上一样的函数名称ﻫ解决方法修正其中的一个函数名称使得函数名称都是独立的ﻫﻫ5 ***WARNING16:UNCALLED SEGMENT,IGNORED FOR OVERLAYPROCESSSEGMENT: ?PR?_DELAYX1MS?DELAY说明DelayX1ms( )函数未被其它函数调用也会占用程序记忆体空间解决方法去掉DelayX1ms()函数或利用条件编译#if …..#endif,可保留该函数并不编译ﻫ6***WARNING6 :XDATASPACE MEMORY OVERLAPFROM : 0025HTO: 0025H ﻫ说明外部资料ROM的0025H 重复定义地址解决方法外部资料ROM 的定义如下Pdata unsigned char XF R_ADC _at_0x25 其中XFR_ADC 变量的名称为0x25,请检查是否有其它的变量名称也是定义在0x25 处并修正它ﻫ7 WARNING206:’DelayX1ms’:missingfunction-prototypeﻫC:\8051\INPUT.CError 267 :’DelayX1ms ‘:requires ANSI-styleprototypeC:\8051\INPUT.C说明程序中有调用DelayX1ms 函数但该函数没定义即未编写程序内容或函数已定义但未作宣告解决方法编写DelayX1ms的内容编写完后也要作宣告或作外部8宣告可在delay.h 的包含档宣告成外部以便其它函数调用ﻫﻫ***WARNING1:UNRESOLVED EXTERNAL SYMBOLﻫSYMBOL:MUSIC3解决办法:1.是文件没有添加到工程里。
LINK2005错误——重复定义错误

LINK2005错误——重复定义错误编程中经常能遇到LNK2005错误——重复定义错误,其实LNK2005错误并不是一个很难解决的错误。
弄清楚它形成的原因,就可以轻松解决它了。
造成LNK2005错误主要有以下几种情况:1.重复定义全局变量。
可能存在两种情况:A、对于一些初学编程的程序员,有时候会以为需要使用全局变量的地方就可以使用定义申明一下。
其实这是错误的,全局变量是针对整个工程的。
正确的应该是在一个CPP文件中定义如下:int g_Test;那么在使用的CPP文件中就应该使用:extern int g_Test即可,如果还是使用int g_Test,那么就会产生LNK2005错误,一般错误错误信息类似:AAA.obj error LNK2005 int book c?book@@3HA already defined in BBB.obj。
切记的就是不能给变量赋值否则还是会有LNK2005错误。
这里需要的是“声明”,不是“定义”!根据C++标准的规定,一个变量是声明,必须同时满足两个条件,否则就是定义:(1)声明必须使用extern关键字;(2)不能给变量赋初值所以,下面的是声明:extern int a;下面的是定义int a; int a = 0; extern int a =0;B、对于那么编程不是那么严谨的程序员,总是在需要使用变量的文件中随意定义一个全局变量,并且对于变量名也不予考虑,这也往往容易造成变量名重复,而造成LNK2005错误。
2.头文件的包含重复。
往往需要包含的头文件中含有变量、函数、类的定义,在其它使用的地方又不得不多次包含之,如果头文件中没有相关的宏等防止重复链接的措施,那么就会产生LNK2005错误。
解决办法是在需要包含的头文件中做类似的处理:#ifndef MY_H_FILE //如果没有定义这个宏#define MY_H_FILE //定义这个宏……. //头文件主体内容…….#endif上面是使用宏来做的,也可以使用预编译来做,在头文件中加入:#pragma once//头文件主体3.使用第三方的库造成的。
重复定义的错误MULTIPLE PUBLIC DEFINITIONS

c/c++语言中有很多地方要用到extern,但是如果没有真正的了解它的意义,会给编程带来很大的麻烦,为了使大家少走弯路,特祥细的说明一下。
对于比较小的程序,一般只有一个c文件和一个头文件,全局变量我们通常会直接定义在c文件中,在程序之前加int i定义。
如果要在头文件中定义有以下两种方法:用extern来声明:extern int i;这一句只是对变量i进行声明,在c文件的程序之前必须加上int i进行定义。
extern int i=0;这一句声明和定义都做了。
对于大一点的程序,有很多c文件和头文件,这个时候全局变量就必须在头文件中声明(不需要初始化),然后在一个c文件中定义(该初始化的要初始化)。
如果在头文件中定义,则编译的时候会出现重复定义的错误。
如果只有头文件中声明就会出现没有定义有警告。
*** ERROR L104: MULTIPLE PUBLIC DEFINITIONSSYMBOL: KMODULE: 222.obj (222)出现上述错误则是因为变量k重复定义,把你的头文件中的变量定义前加extern(只是变量声明不用初始化),再在某一个你要调用该变量的c文件的程序之前再定义(注意第一个调用的c文件要负责附带初始化该变量,其他调用的c文件就不需要初始化过程啦)今天调试时遇到了这个问题,以前都不注意这些警告错误,现在看来争取要做到:0错误,0警告。
中断中和中断外都调用了同一个串口打印函数,一直有上述的报警,没在意,今天调试时发现串口打印出了一些乱七八糟的东东,且程序也不知道跑哪里去了,很郁闷。
最后查到是上述这个问题导致的,现在解决办法为采用第二种办法,第一种办法据说要耗很多存储空间,以后慢慢体会。
说说几个小问题吧,一般我们在用KEIL的时候,只要编译器报- 0 Error(s) 一般我们都不去管多少个 Warning(s).了,一般这样程序基本都能运行,但是其实仔细想想,这里还是有问题的,否则编译器没事吃饱了撑得,报什么警告啊~~~今天来说说*** WARNING L15: MULTIPLE CALL TO SEGMENT这个问题!其实这个问题应该是引起注意的,有可能引起程序冲突,但是一般时候程序运行不会有问题,但是如果出来问题,那将会是很讨厌的问题.分析一下产生这一警告的一个根源是:例如在主循环里调用了一个函数,而在中断服务中,你又一次调用了同样的函数。
命名空间namespace,以及重复定义的问题解析

命名空间namespace,以及重复定义的问题解析名字空间是⽤来划分冲突域的,把全局名字空间划分成⼏个⼩的名字空间。
全局函数,全局变量,以及类的名字是在同⼀个全局名字空间中,有时为了防⽌命名冲突,会把这些名字放到不同的名字空间中去。
⾸先我们看⼀下名字空间的定义:1//MyLib.h⽂件2namespace MyLib{3extern int i;//记住这是变量i的声明,不是i的定义4void fun();5 }//注意名字空间不像类的定义⼀样,右⼤括号后有分号//MyLib.cpp⽂件#include<iostream>#include"MyLib.h"//包含名字空间⽣命所在的⽂件using std::cout;//这是使⽤⽣命,不是使⽤指令using std::endl;int MyLib::i=10;//这是变量i的定义,并且初始化,当然也可以不⽤初始化直接写int MyLib::i;void MyLib::fun(){cout<<i<<endl;}上述代码有⼀个特别需要注意的地⽅就是,在MyLib.h⽂件中,如果使⽤声明,⽆论函数还是变量都必须使⽤声明,如果使⽤定义,⽆论函数还是变量都要使⽤定义。
在MyLib.h中,如果⼀个使⽤声明,另⼀个使⽤定义,那么再接⼝代码分离时会出现重复定义现象,例如//A.h⽂件namespace A{int i;//变量的定义,不是声明void fun();//函数的声明,不是定义,那么函数的定义可以放在A.cpp⽂件中,此时A.cpp⽂件需要#include“A.h“,}1//A.cpp2 #include"A.h"3void A::fun(){456 }//demo.cpp#include"A.h"int main(){return0;}上述三段代码会出现问题,因为A.cpp从新定义了变量i,变量i本来已经在A.h中已经定义,⽽A.cpp中⼜包含了A.h,相当于把A.h中的所有代码复制粘贴到A.cpp中。
解决C++中重定义的方法总结

解决C++中重定义的⽅法总结
C++由于头⽂件重复包含了所定义的变量或者常量,编译器就会报重复定义的错误。
如果你碰见这样的问题可以考虑重下⾯⼏个⽅⾯去解决:
在出现重定义错误的头⽂件加上:
#ifndef FileName_H_
#define FileName_H_
....(头⽂件内容)
#endif
注意如果FileName_H_这个名字已经被使⽤,将会出现未定义问题(这⾥不讨论),这是你保证FileName_H_唯⼀就可以。
在出现重定义错误的头⽂件加上这⼀句:#pragma once 就可以解决(VS建⽴的类都会默认添加这⼀⾏),⽅式2与1其实是⼀样的,⼆选⼀即可(个⼈推荐使⽤⽅式1)
采⽤⽅式1或⽅式2基本上可以解决95%以上的重复定义的问题。
在开发过程中,经常会使⽤第三⽅的API,单独使⽤某⼀个API都正常,但是同时使⽤多个API的时候就会出现某些结构体重复定义的问题,此时可以按照下⾯⼏种⽅式处理:
将重复定义的struct、变量名、常量,提出到⼀个公共的.h⽂件中,然后将原⽂件中公共部分的struct、变量名、常量屏蔽或删除,同时在头⽂件中包含公共的.h⽂件。
如果三防库中,出现C风格、C++风格两种不同的struct定义⽅式,就不能按照3的⽅式解决了(⽅式3解决后编译正常,但是会出现链接问题,分析lib中的导出函数中参数与C风格参数差异)。
此时只需要将C风格⽅式的struct修改为C++风格的struct,同时更新API头⽂件中对应使⽤C风格struct位置。
c++ 结构体 重复定义

c++ 结构体重复定义
在C++中,如果你在同一作用域内多次定义相同名称的结构体,就会导致重复定义错误。
这通常发生在头文件中,如果头文件被包含在多个源文件中,每个源文件都包含相同的结构体定义,就会引起重复定义错误。
为了避免这种错误,你可以使用头文件的预处理器指令来确保结构体只被定义一次。
这可以通过使用条件编译指令(`#ifndef`、`#define`、`#endif`)来实现,例如:
```cpp
// 在头文件中
#ifndef MY_STRUCT_H
#define MY_STRUCT_H
struct MyStruct {
// 结构体成员
int member1;
double member2;
// 其他成员...
};
#endif // MY_STRUCT_H
```
这样,当头文件第一次被包含时,`MY_STRUCT_H`未定义,结构体定义会被包括。
当头文件再次被其他文件包含时,`MY_STRUCT_H`已经被定义,预处理器将跳过结构体的重新定义。
确保你的头文件有适当的保护措施,以避免多次定义相同的结构体。
结构体重复定义

结构体重复定义在编程中,结构体是一种非常重要的数据类型。
结构体定义了一组相关联的数据项,这些数据项可以在一个变量中存储和操作。
但是,当我们在程序中定义多个相同结构体时,就会出现结构体重复定义的问题。
这个问题不仅会导致程序编译错误,还会使程序中出现一些不可预料的错误。
下面,我们将详细了解这个问题,并提供有效的解决方法。
一、什么是结构体重复定义?结构体重复定义指的是在程序中定义了多个相同的结构体。
结构体是一种自定义的数据类型,它包含了一组相关联的数据项。
当我们在程序中定义结构体时,必须要为它定义一个变量名。
如果我们在程序中定义了多个相同的结构体,那么就会出现结构体重复定义的问题。
结构体重复定义的问题常常会导致编译错误。
编译器在编译程序时,会识别出多个相同的结构体定义,从而报告出错信息。
这个问题也会使程序中出现一些未被预计的错误,例如:变量赋值错误、内存分配错误等。
因此,我们必须要注意避免结构体重复定义的问题。
二、结构体重复定义的原因1.文件包含在程序中,我们可以通过#include指令来包含一个头文件。
头文件通常包含一些函数和结构体的定义。
如果我们在多个源文件中包含了同一个头文件,就会出现结构体重复定义的问题。
例如,在两个源文件中都包含了一个名为“Student”的头文件,那么编译器就会在生成可执行文件时识别出两个相同的“Student”结构体。
这样就会导致编译错误。
2.头文件嵌套在头文件中定义的结构体可能会在其他的头文件中继续包含使用。
如果多个头文件都包含了同一个头文件,就会导致结构体重复定义的问题。
例如,在头文件A.h中定义了一个名为“Person”的结构体。
在头文件B.h和C.h中都包含了头文件A.h,那么就会出现两个相同的“Person”结构体,从而导致编译错误。
3.重复定义在程序中,我们可能会在不同的源文件中定义相同的结构体,这也会导致结构体重复定义的问题。
例如,在源文件A.c和B.c中都定义了一个名为“Employee”的结构体。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
<涉及到外部变量的声明>(不是定义,因为不能赋值)c/c++语言中有很多地方要用到extern,但是如果没有真正的了解它的意义,会给编程带来很大的麻烦,为了使大家少走弯路,特祥细的说明一下。
对于比较小的程序,一般只有一个c文件和一个头文件,全局变量我们通常会直接定义在c文件中,在程序之前加int i定义。
如果要在头文件中定义有以下两种方法:用extern来声明:extern int i;这一句只是对变量i进行声明,在c 文件的程序之前必须加上int i进行定义。
extern int i=0;这一句声明和定义都做了。
对于大一点的程序,有很多c文件和头文件,这个时候全局变量就必须在头文件中声明(不需要初始化),然后在一个c文件中定义(该初始化的要初始化)。
如果在头文件中定义,则编译的时候会出现重复定义的错误。
如果只有头文件中声明就会出现没有定义有警告。
*** ERROR L104: MULTIPLE PUBLIC DEFINITIONSSYMBOL: KMODULE: 222.obj (222)出现上述错误则是因为变量k重复定义,把你的头文件中的变量定义前加extern(只是变量声明不用初始化),再在某一个你要调用该变量的c文件的程序之前再定义(注意第一个调用的c文件要负责附带初始化该变量,其他调用的c文件就不需要初始化过程啦)首先纠正你的一个错误,extern是用来声明变量而不是定义变量的当你需要在一个c语言文件中使用另外一个c语言文件中定义的变量时就需要加上extern来声明,这样编译器就知道这个变量是在别的文件中定义的。
比如:你在foo.c中定义了一个全局变量int a=10,你在fff.c中想使用这个变量a,那么你就需要在使用之前在fff.c中用extern声明这个变量:extern int a;1.extern的作用extern有两个作用,第一个,当它与"C"一起连用时,如: extern "C" void fun(int a, int b); 则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的, C++的规则在翻译这个函数名时会把fun这个名字变得面目全非,可能是fun@aBc_int_int#%$也可能是别的,这要看编译器的"脾气"了(不同的编译器采用的方法不一样),为什么这么做呢,因为C++支持函数的重载啊,在这里不去过多的论述这个问题,如果你有兴趣可以去网上搜索,相信你可以得到满意的解释!当extern不与"C"在一起修饰变量或函数时,如在头文件中: extern int g_Int; 它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块或其他模块中使用,记住它是一个声明不是定义!也就是说B模块(编译单元)要是引用模块(编译单元)A中定义的全局变量或函数时,它只要包含A模块的头文件即可, 在编译阶段,模块B虽然找不到该函数或变量,但它不会报错,它会在连接时从模块A生成的目标代码中找到此函数。
2.实例:以上已经说了extern的作用,下面我们来举个例子,如:在test1.h中有下列声明:#ifndef TEST1H#define TEST1Hextern char g_str[]; // 声明全局变量g_strvoid fun1();#endif在test1.cpp中#include "test1.h"char g_str[] = "123456"; // 定义全局变量g_strvoid fun1(){cout << g_str << endl;}以上是test1模块,它的编译和连接都可以通过,如果我们还有test2模块也想使用g_str,只需要在原文件中引用就可以了#include "test1.h"void fun2(){cout << g_str << endl;}以上test1和test2可以同时编译连接通过,如果你感兴趣的话可以用ultraEdit打开test1.obj,你可以在里面着"123456"这个字符串,但是你却不能在test2.obj里面找到,这是因为g_str是整个工程的全局变量,在内存中只存在一份, test2.obj这个编译单元不需要再有一份了,不然会在连接时报告重复定义这个错误!有些人喜欢把全局变量的声明和定义放在一起,这样可以防止忘记了定义,如把上面test1.h改为extern char g_str[] = "123456"; // 这个时候相当于没有extern 然后把test1.cpp中的g_str的定义去掉,这个时候再编译连接test1和test2两个模块时,会报连接错误,这是因为你把全局变量 g_str的定义放在了头文件之后,test1.cpp 这个模块包含了test1.h所以定义了一次g_str,而 test2.cpp也包含了test1.h所以再一次定义了g_str, 这个时候连接器在连接test1和test2时发现两个g_str。
如果你非要把g_str的定义放在test1.h中的话,那么就把test2的代码中#include "test1.h"去掉换成:extern char g_str[];void fun2(){cout << g_str << endl;}这个时候编译器就知道g_str是引自于外部的一个编译模块了,不会在本模块中再重复定义一个出来,但是我想说这样做非常糟糕,因为你由于无法在 test2.cpp中使用#include "test1.h", 那么test1.h中声明的其他函数你也无法使用了,除非也用都用extern修饰,这样的话你光声明的函数就要一大串,而且头文件的作用就是要给外部提供接口使用的,所以请记住,只在头文件中做声明,真理总是这么简单。
2.用static修饰的全局变量首先,我要告诉你static与extern是一对“水火不容”的家伙,也就是说extern和static不能同时修饰一个变量;其次,static修饰的全局变量声明与定义同时进行,也就是说当你在头文件中使用static声明了全局变量后,它也同时被定义了;最后,static修饰全局变量的作用域只能是本身的编译单元,也就是说它的“全局”只对本编译单元有效,其他编译单元则看不到它,如:test1.h:#ifndef TEST1H#define TEST1Hstatic char g_str[] = "123456";void fun1();#endiftest1.cpp:#include "test1.h"void fun1(){cout << g_str << endl;}test2.cpp#include "test1.h"void fun2(){}以上两个编译单元可以连接成功, 当你打开test1.obj时,你可以在它里面找到字符串"123456", 同时你也可以在test2.obj中找到它们,它们之所以可以连接成功而没有报重复定义的错误是因为虽然它们有相同的内容,但是存储的物理地址并不一样,就像是两个不同变量赋了相同的值一样,而这两个变量分别作用于它们各自的编译单元。
也许你比较较真,自己偷偷的跟踪调试上面的代码,结果你发现两个编译单元(test1, test2)的g_str的内存地址相同,于是你下结论static修饰的变量也可以作用于其他模块,但是我要告诉你,那是你的编译器在欺骗你,大多数编译器都对代码都有优化功能,以达到生成的目标程序更节省内存,执行效率更高,当编译器在连接各个编译单元的时候,它会把相同内容的内存只拷贝一份,比如上面的"123456", 位于两个编译单元中的变量都是同样的内容,那么在连接的时候它在内存中就只会存在一份了,如果你把上面的代码改成下面的样子,你马上就可以拆穿编译器的谎言:test1.cpp:#include "test1.h"void fun1(){g_str[0] = 'a';cout << g_str << endl;}test2.cpp#include "test1.h"void fun2(){}void main(){fun1(); // a23456fun2(); // 123456}这个时候你在跟踪代码时,就会发现两个编译单元中的g_str地址并不相同,因为你在一处修改了它,所以编译器被强行的恢复内存的原貌,在内存中存在了两份拷贝给两个模块中的变量使用。
正是因为static有以上的特性,所以一般定义static全局变量时,都把它放在原文件中而不是头文件,这样就不会给其他模块造成不必要的信息污染,同样记住这个原则吧!3 const修饰的全局常量const修饰的全局常量用途很广,比如软件中的错误信息字符串都是用全局常量来定义的。
const修饰的全局常量据有跟static相同的特性,即它们只能作用于本编译模块中,但是const可以与extern连用来声明该常量可以作用于其他编译模块中, 如extern const char g_str[];然后在原文件中别忘了定义:const char g_str[] = "123456";所以当const单独使用时它就与static相同,而当与extern一起合作的时候,它的特性就跟extern的一样了!所以对const我没有什么可以过多的描述,我只是想提醒你,const char* g_str = "123456" 与 const char g_str[] = "123465"是不同的,前面那个const修饰的是char * 而不是g_str,它的g_str并不是常量,它被看做是一个定义了的全局变量(可以被其他编译单元使用),所以如果你像让char *g_str遵守const的全局常量的规则,最好这么定义const char* const g_str="123456".。