8

I am experiencing some strange behavior with Visual Studio 2010 and C++. I have a header file in which I declare some global constants

#ifndef CONSTANTS_H
#define CONSTANTS_H

#define WIN32_LEAN_AND_MEAN

// Macros...
#define SAFE_RELEASE(ptr) { if (ptr) { ptr->Release(); ptr = NULL } }
#define SAFE_DELETE(ptr) { if (ptr) { delete ptr; ptr = NULL; } }

// Constants...
const char* CLASS_NAME = "WinMain";
const char GAME_TITLE[] = "DirectX Window";

const int GAME_WIDTH = 640;
const int GAME_HEIGHT = 480;

#endif

My problem comes in with the following line:

const char* CLASS_NAME = "WinMain";

When it's like this, and I build my solution I get the following 2 errors:

error LNK1169: one or more multiply defined symbols found, and

error LNK2005: "char const * const CLASS_NAME" (?CLASS_NAME@@3PBDB) already defined in graphics.obj

Now is strange since a ran a 'find in files' and I definitely do not declare it somewhere else ie no duplicate declarations.

Should I change it to:

const char* const CLASS_NAME = "WinMain";

OR

const char CLASS_NAME[] = "WinMain";

It compiles just fine! But as far as I know char* x is equivalent to char x[], and the fact that I'm enforcing 'const-ness' on both the pointer and the pointed-to value should make no difference.... or does it?

I'm a bit new to C++ development on the Windows platform, so any help will be greatly appreciated!

Blachshma
  • 17,097
  • 4
  • 58
  • 72
mnemonic
  • 692
  • 1
  • 9
  • 18
  • The SAFE_RELEASE macro and it's friends are *not safe at all*. Use smart pointers, don't `delete` or `Release()` yourself. – Puppy Dec 07 '12 at 23:37
  • possible duplicate of [error LNK1169: one or more multiply defined symbols found](http://stackoverflow.com/questions/12132453/error-lnk1169-one-or-more-multiply-defined-symbols-found) – Raymond Chen Dec 07 '12 at 23:39

3 Answers3

7

You mistake is that you did not declare your constants as constants. In C++ syntax (as well as in C) in order to declare a constant pointer you have to do this

const char* const CLASS_NAME = "WinMain";

Note that your GAME_TITLE, GAME_WIDTH and GAME_HEIGHT are correctly declared as constants, which is why they give you no trouble. Only the CLASS_NAME is declared incorrectly, i.e. as non-constant.

Constants in C++ have internal linkage by default, which is why you can define them in header files without violating the One Definition Rule.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 1
    Thanks!! Just had an Eureka-moment! :) – mnemonic Dec 07 '12 at 23:44
  • 1
    Wow, I never thought it can be a problem. Thanks a lot :) – Seongeun So Sep 05 '14 at 02:20
  • 1
    can someone explain why you need "const" twice though? Like, is this because this is a constant pointer to a constant character array, or something similar? – aquirdturtle Sep 15 '15 at 20:07
  • 1
    @aquirdturtle: You would read that from right to left so it's a 'constant pointer to a constant char'. So neither the pointer nor the value pointed to by that pointer can change. – mnemonic Apr 07 '16 at 11:31
5

Don't define variables in headers. When you include that header in more than one translation unit, you have multiple copies of the definition.

Only declare variables there (using extern), and define them in precisely one translation unit.

That said... an exception to this rule can be made for constants of built-in types, since they have internal linkage by default.

That is, the two programs are functionally identical:

const int x = 42;
int main() {}

static const int x = 42;
int main() {}

This ensures that one copy is generated for each translation unit, avoiding the issue altogether.

I think the same is true for arrays. So, sure, const it up and knock yourself out.


But as far as I know 'char* x' is equivalent to 'char x[]',

Only in a function parameter list. In a fully-fledged declaration, x will take the array type with dimension determined from the initialiser.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Thanks for the reply! :) Just a question on your comment "When you include that header in more than one translation unit, you have multiple copies of the definition." isn't the #include guards supposed to suppress that kind of behavior? – mnemonic Dec 07 '12 at 23:14
  • And I got this cool article which alleviated some of the misconceptions I had about char pointers: http://www.cs.bu.edu/teaching/cpp/string/array-vs-ptr/ link :) – mnemonic Dec 07 '12 at 23:20
  • @mnemonic: No. Include guards can only function _within_ a translation unit. That is, they prevent problems caused by `#include "hi.h" \n #include "hi.h"` in the same .cpp file, or more complex equivalents reached by headers including other headers... The include guards, like the rest of your C++ code, don't exist any more at the link stage. Consider the collections `{1,2,3,3}` and `{3,4,5,5,6,6}` you can remove duplicates in each collection to get `{1,2,3}` and `{3,4,5,6}`, but when you perform your final step of merging those collections you still have a duplicate: `{1,2,3,3,4,5,6}`. – Lightness Races in Orbit Dec 08 '12 at 12:59
  • Also cf declare/define in [this example](https://stackoverflow.com/a/48987648/2128797) with structs. Hope it helps.:) – Laurie Stearn Feb 26 '18 at 11:46
1

You get the error because if you include the file in multiple compilation units, you are declaring a variable of that name in each unit. Compilation is not a problem, but when the linker tries to link it sees multiple definitions of the same variable. You can get away with this with const int variables and const char GAME_TITLE[] because they have static linkage, but for string literals (which are essentially char arrays) you will get this error. You should be able to fix the problem using extern

mathematician1975
  • 21,161
  • 6
  • 59
  • 101