2

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?
Peter
  • 331
  • 3
  • 11
  • You can get rid of the linker error by declaring USB_MANAGER_DBUS_SERVICE as `const char* const USB_MANAGER_DBUS_SERVICE = "com.USBService";` – selbie Aug 28 '14 at 08:49
  • 1
    One is const, the other isn't... – Kerrek SB Aug 28 '14 at 08:52
  • #define only does text substitution at pre-processing stage. The variable USB_MANAGER_DBUS_OBJ_PATH does not exist beyond this stage. So no question of link error. Any variable with a const qualifier in c++ has an internal linkage. That means its scope is only in the file in which it is declared ( like static ). Therefore there is no link error. – Vimal Aug 28 '14 at 09:21

5 Answers5

4

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.

Community
  • 1
  • 1
Niall
  • 30,036
  • 10
  • 99
  • 142
1

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.

perreal
  • 94,503
  • 21
  • 155
  • 181
1

"#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.

RedXIII
  • 781
  • 7
  • 6
0

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.

Dr. Debasish Jana
  • 6,980
  • 4
  • 30
  • 69
  • However USB_MANAGER_DBUS_SERVICE should be a const pointer not just a pointer to const, so put in an extra const. Easier just to use [] – CashCow Aug 28 '14 at 09:07
0
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.

T.C.
  • 133,968
  • 17
  • 288
  • 421
CashCow
  • 30,981
  • 5
  • 61
  • 92