可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a header file foo.h
:
#ifndef __FOO_H__
#define __FOO_H__
const char* USB_MANAGER_DBUS_SERVICE = "com.USBService";
#define USB_MANAGER_DBUS_OBJ_PATH "/com/USB/MgrObject"
const int DBUS_CONNECTION_MAX_RETRY_TIME = 5;
#endif
And many cpp files that includes foo.h
.
foo.c bar.c
When compiling them together, "multiple definition error" appears.
Linking CXX shared library:
foo.cpp.o:(.data.rel.local+0x0): multiple definition of `USB_MANAGER_DBUS_SERVICE'
bar.cpp.o:(.data.rel.local+0x0): first defined here
So I have two questions below:
- Why doesn't
#define
lead to a link error?
- Why doesn't
const int
lead to a link error?
回答1:
The #define
for USB_MANAGER_DBUS_OBJ_PATH
is constant across compilation units, it is a text substitution.
So is the const int
for DBUS_CONNECTION_MAX_RETRY_TIME
constant across TU. The const
makes the variable read only, it's essentially not declaring it as a modifiable lvalue, it has an implicit internal linkage, from these posts.
const char* USB_MANAGER_DBUS_SERVICE = "com.USBService";
Why does USB_MANAGER_DBUS_SERVICE
cause a linker error?
It is not const
, as in the pointer is not a constant value, only what is being pointed to.
const char* const USB_MANAGER_DBUS_SERVICE = "com.USBService";
// ^^^^^ added const
Would be const
.
回答2:
Define doesn't cause a link error because the preprocessor is just pasting the string literal into different parts of the code instead of referring to some object defined in some object file.
const int
does not cause a link error because const
implies internal linkage, so you end up having a constant per compilation unit.
You can fix the redefinition error by defining extern const char* USB_MANAGER_DBUS_SERVICE;
in the header file and defining const char* USB_MANAGER_DBUS_SERVICE = "..."
in one of your source files.
回答3:
"#define"d tokens are already processed by the preprocessor, even before the compiler comes into play.
In C++ and "const int" values are evaluated at compile time, i.e. a "const int" is equivalent to a typesafe define (and thus should be preferred over #define).
The only symbol the compiler actually creates for the linker is "USB_MANAGER_DBUS_SERVICE", because only the target to which the pointer points to is "const", but not the pointer itself.
回答4:
Here's the problem. By having #ifndef FOO_H wrapper, you are avoiding multiple inclusion of foo.h in your .c or .cpp file, but when linker links all different object files together, you have multiple definitions.
Remedy is you use like the following in foo.h:
#ifndef __FOO_H__
#define __FOO_H__
extern const char* USB_MANAGER_DBUS_SERVICE;
#define USB_MANAGER_DBUS_OBJ_PATH "/com/USB/MgrObject"
extern const int DBUS_CONNECTION_MAX_RETRY_TIME;
#endif
And, in only one .c or .cpp file (i.e one source file), use the following:
const char* USB_MANAGER_DBUS_SERVICE = "com.USBService";
const int DBUS_CONNECTION_MAX_RETRY_TIME = 5;
This will solve your multiple definition problem complained by the linker.
回答5:
const char* USB_MANAGER_DBUS_SERVICE = "com.USBService";
The string this pointer points to is const, but the pointer is not const, thus it is a multiple definition. Change it to
const char USB_MANAGER_DBUS_SERVICE[] = "com.USBService";
There is no linkage error for the const int because it is a real constant, not a const-modifier. It is therefore considered static
data to the application.
Incidentally if you put in another const for the pointer
const char* const USB_MANAGER_DBUS_SERVICE = "com.USBService";
Now that pointer is a proper constant too, but I'm not sure if it will work. You can declare it as extern
and then define it in one compilation unit, but the simplest is the first syntax I showed you.