Why multiple definition error in C++ not caused by

2019-04-15 11:20发布

问题:

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:

  1. Why doesn't #define lead to a link error?
  2. 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.



标签: c++ linker