On the language level, string literals and numeric literals are different beasts.
The C and C++ standard essentially specify that string literals are treated "as if" you defined a constant array of characters with the appropriate size and content, and then you used its name in place of the literal. IOW, when you write
const char *foo = "hello";
it's as if you wrote
// in global scope
const char hello_literal[6] = {'h', 'e', 'l', 'l', 'o', '\0'};
...
const char *foo = hello_literal;
(there are some backwards-compatibility exceptions that allow you to even write char *foo = "hello";
, without the const
, but that's deprecated and it's undefined behavior anyway to try to write through such a pointer)
So, given this equivalence it's normal that you can have the address of the string literal. Integral literals, OTOH, are rvalues, for which the standard specifies that you cannot take any address - you can roughly think of them as values that the standard expect not to have a backing memory location in the conventional sense.
Now, this distinction actually descends from the fact that on the machine level they are usually implemented differently.
A string literal generally is stored as data somewhere in memory, typically in a read-only data section that gets mapped in memory straight from the executable. When the compiler needs its address it's easy to oblige, since it is data stuff that is already in memory, and thus it does have an address.
Instead, when you do something like
int a = 5;
the 5
does not really have a separate memory location like the "hello world"
array above, but it's usually embedded into the machine code as an immediate value.
It's quite complicated to have a pointer to it, since it would be a pointer pointing halfway into an instruction, and in general pointing to data in a different format than what be expected for a regular int
variable to which you can point - think x86 where for small numbers you use more compact encodings, or PowerPC/ARM and other RISC architectures where some values are built from an immediate manipulated by the implicit barrel shifter and you cannot even have immediates for some values - you have to compose them out of several instructions, or Harvard architectures where data and code live in different address spaces.
For this reason, you cannot take the address of numeric literals (as well as of numeric expressions evaluation results and much other temporary stuff); if you want to have the address of a number you have to first assign it to a variable (which can provide an in-memory storage), and then ask for its address.