2

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?

Update: here's my own answer (added 6 Oct. 2023)

This is what constexpr is for in C++, so this is the best way in C++:

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

Unlike const, which could be a run-time limitation on variable usage and modification, constexpr variables must be known statically at compile-time. They are the right way to do this in this case.

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
3
  • Possible duplicate of static const vs #define Commented Oct 10, 2018 at 5: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) Commented Oct 10, 2018 at 13:04
  • 1
    Re "In C, apparently you're stuck with #define": No, there is the 'enum' trick. Commented Oct 5, 2023 at 5:16

3 Answers 3

8

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++.

Sign up to request clarification or add additional context in comments.

3 Comments

So to answer the Q in the title: no, quite the contrary.
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).
@GabrielStaples: As an embedded systems programmer who worked mainly in C, I can understand what you are saying.
1

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 <windows.h>), and the absence of namespaces as this is all a 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, and C++14 is recommended when you want to write constexpr functions. For constexpr standard library functions, you'll have to wait for C++20, if I remember well.

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

6 Comments

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 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.
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?
I've must have missed that, let me remove that from the answer
Tough world; sometimes we all get downvotes for trying (note: I'm also not your downvoter). I am talking about embedded work though FYI.
|
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.

The preprocessor macro is replaced at compile time, and that's why it's working fine.

Reference: Why is int x[n] wrong where n is a const value?

2 Comments

They cannot be modified by the program. To even attempt it makes your program have undefined behavior.
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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.