Is it bad practice to specify an array size using

2020-03-31 06:02发布

问题:


Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.

Closed 2 years ago.

In C, declaring an array size using a variable, even if it is a const variable, is NOT allowed. Ex: 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.

Output:

$gcc -o main *.c
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++.

Output:

$g++ -o main *.cpp
$main
1, 2

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

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 have no other choice. But in C++ you clearly have at least 2 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. https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es31-dont-use-macros-for-constants-or-functions
  4. "static const" vs "#define" vs "enum"
  5. https://en.wikipedia.org/wiki/Variable-length_array#C99

回答1:

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



回答2:

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



回答3:

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];