1

In C, declaring an array size using a variable, even if it is a const variable, is not allowed. Example: this fails to compile in C:

#include <stdio.h>

const int SIZE = 2;
int a[SIZE];

int main()
{
    a[0] = 1;
    a[1] = 2;
    printf("%i, %i", a[0], a[1]);
    return 0;
}

Run this code in C.

gcc -o main *.c

Output:

main.c:5:5: error: variably modified ‘a’ at file scope
 int a[SIZE];
     ^

In C++, however, it runs just fine.

Run the above code in C++.

g++ -o main *.cpp
main

Output:

1, 2

To make it run in C, you must use #define instead of a variable. I.e.:

This runs just fine in C or C++:

#include <stdio.h>

#define SIZE 2
// const int SIZE = 2;
int a[SIZE];

int main()
{
    a[0] = 1;
    a[1] = 2;
    printf("%i, %i", a[0], a[1]);
    return 0;
}

Run this code in C.

So, in C++ I've almost always used a variable, rather than #define, to declare my array sizes. I just make the array size variable const and it's all good! Recently I started doing a lot of microcontroller programming in pure C, however, and when I ran into this error and figured out the problem, a senior developer told me it's bad practice to use anything but #define-ed constants (or maybe hard-coded numbers) to declare array sizes.

Is this true? Is it bad practice in C++ to use const variables instead of #define when specifying array sizes? If so, why?

In C, apparently you're stuck with #define: you don't have any other choice. But in C++ you clearly have at least two choices, so is one better than the other? Is there a risk to using one over the other?

Related:

  1. Variably modified array at file scope in C
  2. static const vs #define <-- this is a solid question and very helpful. It is most definitely related to my question, but my question is *not a duplicate because although they are both about const vs #define, my question is a very special case where one of the options doesn't even work in a language which is regularly considered to be a subset of C++. That's pretty unusual, and makes my question a more narrow subset which fits within the broad scope of this other question. Therefore, not a duplicate.
  3. C++ Core Guidelines, ES.31: Don't use macros for constants or "functions"
  4. "static const" vs "#define" vs "enum"
  5. Variable-length array, C99
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
  • Possible duplicate of [static const vs #define](https://stackoverflow.com/questions/1637332/static-const-vs-define) – DYZ Oct 10 '18 at 05:42
  • 1
    @SamuelLiew: I think you may have moved one comment too many. n.m. made a direct comment - variable-length arrays **are** valid C, in direct contradiction of the very premise of this question. (Which is unrelated to the C versus C++ discussion, which I agree belongs in chat) – MSalters Oct 10 '18 at 13:04

3 Answers3

7

It would be good to follow Scott Meyer's advice in this matter. From his book "Effective C++":
Item 2: Prefer consts, enums, and inlines to #defines.

Summary of the item adapted to your example.

This Item might better be called “prefer the compiler to the preprocessor,” because #define may be treated as if it’s not part of the language per se. That’s one of its problems.

When you do something like this,

#define SIZE 2

the symbolic name SIZE may never be seen by compilers; it may be removed by the preprocessor before the source code ever gets to a compiler. As a result, the name SIZE may not get entered into the symbol table. This can be confusing if you get an error during compilation involving the use of the constant, because the error message may refer to 2, not SIZE . If SIZE were defined in a header file you didn’t write, you’d have no idea where that 2 came from, and you’d waste time tracking it down. This problem can also crop up in a symbolic debugger, because, again, the name you’re programming with may not be in the symbol table. The solution is to replace the macro with a constant:
const double SIZE = 2; // uppercase names are usually for macros, hence the name change
As a language constant, SIZE is definitely seen by compilers and is certainly entered into their symbol tables.

✦ For simple constants, prefer const objects or enums to #defines.
✦ For function-like macros, prefer inline functions to #defines.

Also refer to "Item 3: Use const whenever possible." for more info on its usage and exceptions to its usage.

So to answer your question in the title:
No, it is NOT a bad practice to specify an array size using a variable instead of #define in C++.

P.W
  • 26,289
  • 6
  • 39
  • 76
  • 4
    So to answer the Q in the title: no, quite the contrary. – StoryTeller - Unslander Monica Oct 10 '18 at 05:34
  • I think this thought process that "`#define` is the way to go" must certainly be an ingrained habit and essentially a "ritualistic belief" (ie: believed it is the most correct simply because one has been doing it for years) then from years of writing in pure C. For pure C, I'll use #define for array sizes, as apparently I have no other options. For embedded *C++*, I'll use `const`. Besides, I'm more used to `const` and like it better anyway :) (in small part due to my own form of "ritualistic belief", and in large part because of answers like this). – Gabriel Staples Oct 10 '18 at 06:16
  • @GabrielStaples: As an embedded systems programmer who worked mainly in C, I can understand what you are saying. – P.W Oct 10 '18 at 06:22
0

Because SIZE was defined as a const variable. In C, unlike C++ const values are not true constants. They are stored in memory and can be referenced and modified indirectly (by modifying the contents in memory). That's why, in C++ there is const that would allow const int SIZE = 2; to work, but even const is not enough in C.

C11-§6.7.9/3:

The type of the entity to be initialized shall be an array of unknown size or a complete object type that is not a variable length array type.

Preprocessor macro replace at compile time, that's why it's work fine.

Reference Link : https://stackoverflow.com/a/35162075/5612562

msc
  • 33,420
  • 29
  • 119
  • 214
  • 1
    They cannot be modified by the program. To even attempt it makes your program have undefined behavior. – StoryTeller - Unslander Monica Oct 10 '18 at 05:26
  • If a variable is const in your unit, you are not not allowed to change it, however if a extern thing can change it you can declare it as volatile but still if it's const you are not allowed yourself to change it. – Stargateur Oct 10 '18 at 05:48
0

I like your question! C++ is evolving to a language where you no longer need #define. Depending on the version, you can replace other constructs. Their are several reasons for this, including problems with IDEs, unexpected replacement of variable names of enum values with other constants, (Don't combine ERROR and ) and the absence of namespaces as this is all preprocessor replacement.

For this example, you create a C-style array, which requires a constant. Writing const int SIZE = 2 however does not create a constant, it creates a immutable variable. This can be used in a lot of ways and can be initialized with almost any code, including something you calculated from reading a file.

The thing you are searching for is constexpr. The keyword that promotes your variable to a compile time constant and limits the initialization options to all code that can be executed at compile time. So: constants, calculations with them and constexpr functions that group these calculations. If you want to know more about it, Google for constexpr (or use a different search engine).

Note C++11 is required for constexpr, C++14 is recommended when you want to write constexpr functions. For constexpr standard library functions, you'll have to wait C++20, if I remember well.

constexpr int SIZE = 2;
int a[SIZE];
JVApen
  • 11,008
  • 5
  • 31
  • 67
  • I share the love for `std::vector` but it is often a poor choice when writing for a lightweight embedded system where memory fragmentation is a problem. There are, of course, ways around this, but they may not be worth the effort or the neutering of `vector`. – user4581301 Oct 10 '18 at 05:57
  • @user4581301 Agreed, however, I don't know if the user writes for embedded. Custom allocators that use a pool can still work, so does std::array. However, for the code I write daily, std::vector is the default. – JVApen Oct 10 '18 at 05:59
  • 2
    From the question: *Recently I started doing a lot of microcontroller programming*. I'n not the downvoter mind you. That was just a comment left for those who follow. I think the downvote is because the answer kind of focusses on minutia and misses the point of the question: **Is it bad practice in C++ to use const variables instead of #define when specifying array sizes? If so, why?** – user4581301 Oct 10 '18 at 06:06
  • I've must have missed that, let me remove that from the answer – JVApen Oct 10 '18 at 06:22
  • Tough world; sometimes we all get downvotes for trying (note: I'm also not your downvoter). I am talking about embedded work though FYI. – Gabriel Staples Oct 10 '18 at 06:24
  • Fantastic point about the language evolving and allowing for far more expressive constant specification. My [favorite example of this in C++17](https://godbolt.org/z/mmz2nz). – StoryTeller - Unslander Monica Oct 10 '18 at 06:51