88

I know it's quite idiomatic, or good style at least, in C to declare numeric constants as enums instead of #defineing them.

/* bad style */
#define MAXLINE 1024

/* good/better style */
enum {
    MAX_LINE = 1024
};

Is there an equivalent rule for the definition of string constants?

/* is this good style? */
#define HELLO "Hello World"

/* or is this better? */
const char *HELLO2 = "Howdy";

What do you prefer? If possible show some drawbacks of either method.

Chris Tang
  • 567
  • 7
  • 18

5 Answers5

116

There's one more (at least) road to Rome:

static const char HELLO3[] = "Howdy";

(static — optional — is to prevent it from conflicting with other files). I'd prefer this one over const char*, because then you'll be able to use sizeof(HELLO3) and therefore you don't have to postpone till runtime what you can do at compile time.

The define has an advantage of compile-time concatenation, though (think HELLO ", World!") and you can sizeof(HELLO) as well.

But then you can also prefer const char* and use it across multiple files, which would save you a morsel of memory.

In short — it depends.

Michael Krelin - hacker
  • 138,757
  • 24
  • 193
  • 173
  • Thanks for your explanation. The reason to use `enum`s instead of macro constants or `const int` for constant integer values is the fact this is the only type-safe way to declare a constant integer which can be portable used as an array boundary. Well... The compile time string concatenation almost selled me on using `#define`s for strings but I'll stick to `const char*` for type safety using `static` where applicable. In short I'll use `enum` for integer constants and `const` variables for all the rest. –  Sep 17 '09 at 07:05
  • 1
    tatt, I'd suggest that you don't stick to anything. *Not all constants are equal*. If you're not going to concatenate, you don't need #define, if you're not going to need the size either, you don't care at all. What I totally fail to see is how `const char*` is better then `const char []` *where static is applicable*. But then again, if you don't need the size, it's not any worse either ;-) – Michael Krelin - hacker Sep 17 '09 at 07:29
26

One advantage (albeit very slight) of defining string constants is that you can concatenate them at compile time:

#define HELLO "hello"
#define WORLD "world"

puts( HELLO WORLD );

Not sure that's really an advantage, but it is a technique that cannot be used with const char *'s.

William Pursell
  • 204,365
  • 48
  • 270
  • 300
17

If you want a "const string" like your question says, I would really go for the version you stated in your question:

/* first version */
const char *HELLO2 = "Howdy";

Particularly, I would avoid:

/* second version */
const char HELLO2[] = "Howdy";

Reason: The problem with second version is that compiler will make a copy of the entire string "Howdy", PLUS that string is modifiable (so not really const).

On the other hand, first version is a const string accessible by pointer HELLO2, and it can not be modified.

Viren
  • 2,161
  • 22
  • 27
  • 4
    I think there are two errors in this answer: 1) The second version creates a constant string, it is not modifiable, at least on systems that protect their memory. There will be no copying, if this variable is static. 2) The pointer is not constant, only the characters it points to. To get a read-only pointer, you need to write `const char * const HELLO2 = "Howdy";` – the busybee Mar 01 '20 at 14:33
  • IntelliSense: `this constant expression has type "const char *" instead of the required integral or enum type` – Alexis Oct 29 '20 at 08:54
9

The main disadvantage of the #define method is that the string is duplicated each time it is used, so you can end up with lots of copies of it in the executable, making it bigger.

Rik Heywood
  • 13,816
  • 9
  • 61
  • 81
  • 7
    I believe compiler will optimize them out at least as long as they're used in the same file. So that's not really "each time it is used", but yes, defining global constant does save space sometimes. – Michael Krelin - hacker Sep 16 '09 at 08:07
  • 3
    that is true, unless the compiler or linker collapses the copies into one. This feature is also sometimes called "string pooling" – Rom Sep 16 '09 at 08:08
  • Actually they all point to the same address. Try to use char * foo = "HELLO WOLRD" and char * bar = "HELLO WORLD"; Then you will find foo == bar to be true. I believe this design pattern is called Flyweight. – Cameron Monks May 20 '20 at 21:31
1

Their are a few differences.

#define HELLO "Hello World"

The statement above can be used with preprocessor and can only be change in the preprocessor.

const char *HELLO2 = "Howdy";

The statement above can be changed with c code. Now you can't change the each individual character around like the statement below because its constant.

HELLO2[0] = 'a'

But you what you can do is have it point to a different string like the statement below

HELLO2 = "HELLO WOLRD"

It really depends on how you want to be able to change the variable around. With the preprocessor or c code.

Cameron Monks
  • 107
  • 1
  • 7