Symbolic Constant Conundrum The article explains that symbolic constants, which replace literal values with named symbols, are used in programming to avoid "magic numbers" and improve code clarity. It details the evolution of methods for defining these constants in C and C++, including `#define` macros, `const`, `enum`, and `constexpr`, noting that `constexpr` (available in C++11 and C23) is the best option because it creates true constants, while `#define` should be used only as a last resort due to scope issues. Introduction A symbolic constant in any programming language is a name — a symbol — that can be used to stand in for a constant — a literal value. Programming languages inherited symbolic constants from mathematics that has many of them grouped by specific field of study. Examples include: π pi , c speed of light , e Euler’s number , G gravitational constant , h Plank’s constant , etc. While those exact constants can be defined and used in programs, many programs define program-specific constants. Using constants is better than using magic numbers https://en.wikipedia.org/wiki/Magic number programming . Both C and C++ have acquired multiple ways to specify symbolic constants as their respective languages have evolved over the decades, namely: - Macros via define . - Enumerations. - const . - constexpr . Knowing which of the ways to use in a particular case can be quite the conundrum. Macros Originally, C only had macros https://dev.to/pauljlucas/cc-preprocessor-macros-fh5 , specifically, object-like macros, e.g.: define BUF SIZE 8192 Macros are adequate, but not good . Why? Macros in general ignore scope, so you typically have to give macros very specific long names to avoid collision. Enumerations Enumerations in C https://dev.to/pauljlucas/enumerations-in-c-ae7 and C++ https://dev.to/pauljlucas/enumerations-in-c-pn9 are better, especially for declaring a set of related constants. In C++ with enum class , they can even be scoped to avoid collisions; in C, however, they’s still in the global scope. The other caveat is that they can be constants only for integral values. const As I described for C https://dev.to/pauljlucas/c-const-conundrum-j2l and C++ https://dev.to/pauljlucas/const-conundrum-2bfj , you can use const for constants, e.g.: js static unsigned const BUF SIZE = 8192; char BUF BUF SIZE ; int main { char local buf BUF SIZE ; // ... } In C++, that will compile just fine without warning; in C, it’ll either be accepted with warnings or rejected entirely, especially if you disable language extensions. Why? Because const is a misnomer since it really means immutable , not constant , and C is more picky about it. While the declaration of BUF might be accepted, the declaration of local buf will either be considered a variable length array VLA that, as I pointed out https://dev.to/pauljlucas/obscure-c99-array-features-3270 , you should probably never use , or rejected since VLAs are an optional feature and not all compilers support them notably, Microsoft’s C compiler doesn’t . A common work-around in C prior to C23, see below is to ab use enum : enum { BUF SIZE = 8192 }; That is, use a nameless enumeration. The advantage is that enumeration constants really are constant . constexpr If you’re using C++11 or later, or C23 or later, there’s constexpr . Unlike const , constexpr really means constant . This is by far the best option for declaring constants: constexpr unsigned BUF SIZE = 8192; Conclusion To summarize: - If you’re declaring a set of related, integral constants, use enum in C or enum class in C++ . - Otherwise, use constexpr if you can. - Otherwise, use const . - Otherwise, use define as a last resort.