For C, the first edition of The C Programming Language (a.k.a. K&R) suggests that your intuition about preprocessor macros is correct:
Symbolic constant names are commonly written in upper case so they can be readily distinguished from lower case
variable names.
In many ways, this was a holdover from assembly language, where macros were defined in uppercase along with labels, opcodes, register names and everything else. The advent of AT&T-style assembly changed that on some platforms, but I think it was heavily influenced by the fact that terminals supporting lowercase were becoming a thing and Unix was what I'd call a "lowercase operating system."
On the other two points, you're batting .500:
Enum
By the time the second edition was published, enum
s had been defined, they were referred to as enumeration constants and covered in the section on constants. Because the constants defined by an enum
are represented by symbols, that makes them symbolic constants, which, if you're going to follow the recommended convention, should be named in uppercase. (Like preprocessor macros, nothing stops you from doing otherwise.)
The implication here is that unlike some languages that followed, C does not treat enum
as a first class type where the types themselves are distinct, enumeration values are specific to each type and may be re-used in others. Instead, it's effectively a convenient shorthand for #define
that produces sequences of integers with identifiers attached. This makes
enum foo { BAR, BAZ };
enum quux { BLETCH, BAZ };
invalid because all of the symbols share scope and BAZ
is redefined. (This was an improvement over the preprocessor which, at the time, didn't warn about one #define
clobbering another.) Further, C doesn't care whether you mix them because they're all just integers, making
enum foo { BAR, BAZ };
enum quux { BLETCH, BLRFL };
enum foo variable = BLETCH;
completely valid even on a modern compiler with all of the warnings turned on.
Const
NB: The const
keyword originated in 1981 with Stroustrup's C With Classes (which evolved into C++) and was eventually adopted by C. The choice of name is unfortunate, because it collides with K&R's use of the term constant to mean what we would now call a literal (e.g., 38
, 'x'
or "squabble"
). The text in the second edition wasn't rewritten to reflect that.
Variables declared const
are a different story because they're still variables. They're not supposed to be modified, but whether the notion of a constant variable makes any more sense than, say, jumbo shrimp is fodder for another discussion. Whatever the case, C was never really serious about it because the standard only requires that the compiler emit a diagnostic when you attempt to change one. The actual behavior is undefined if a modification is compiled and executed.
Being variables, it makes sense that anything const
would follow the convention of being named in lowercase. Using them to represent literals does have some type safety advantages but doesn't make for good optimization if you have to reach between compilation units to get the value.
What they aren't is constants in the K&R sense, and for that reason their identifiers shouldn't be uppercase. Some people use them that way, but it isn't a practice I recommend except in a few specific cases.
const
identifiers being lower case and#defines
as upper. Java though adopted upper case for constants and other languages followed, but I could have that last bit wrong. More research needed! :)