hgm wrote:This is why I preferred the #define method. You can probably achieve the same with 'const':
const int *nPcSq = &evalTerm;
etc. Then the compiler will know at all times that it is just a fixed address.
No, that's not necessarily a fixed address. You probably meant
int const * const nPcSq = &evalTerm;
This first `const' means that the `int's being pointed to are constant. The second `const' means the address `nPcSq' points to is constant.
The order of the `int' and the first `const' doesn't matter. I prefer the order I used because you can then read the type backwards from the variable name: "A constant pointer to constant ints".
Actually that's not quite what const means. Const gives us a way to have read access to something for a while, without also being granted permission to write to it. So if my function accepts a const Foo* then callers can think of my function as promising to "maybe read from, but not write to" whatever Foo object they pass to it. Without const, the ability to read and ability to write were always paired together. But if const is being consistently used throughout the program, then when a function accepts a Foo* the caller can think of that as "this function needs to write to the Foo". Similarly, if you mark all of your local variables as const except the ones that you actually need to modify, then suddenly 90% of your variables are const and very easy to reason about, and you only have to worry about the other 10% when reviewing or auditing the code.
So you can use it to express your intention to human readers, and make your code clearer and easier to reason about. But it is mostly useless to the optimizer, because of aliasing.
If two different pointers A and B of compatible types point to the same storage, then writes to *A will be visible when you read *B, even if B's type was "const int* B". That declaration basically means "B is a pointer to some int(s) which I don't want to write to through B
", however it can't guarantee that some other code somewhere
doesn't write to that same int through some other pointer. A compiler can do alias analysis and sometimes it can prove that at the point where you use B, nothing else is aliased to it (or some other known pointer is definitely aliased to it). But const won't help it at all for this kind of analysis. Even local variables can be aliased, if you take the address of the variable and that address "escapes" to a global context (e.g. if it was passed as a parameter to some function that the optimizer can't see the source to and doesn't know the semantics of).
Any two pointers to compatible types might or might not alias, and unless the optimizer can prove that its one or the other, it has to conservatively generate code that works correctly for both cases.
So basically, our promise to not modify it is unnecessary when there's no aliasing happening, and its not strong enough to be useful to the optimizer when there is (or might be) aliasing happening.
Const can be a bit subtle. There's a distinction between "logical constness" and "physical constness", but there's only one keyword, and they usually work out to the same thing, except when they don't.
The keyword is actually a "type qualifier" just like volatile (together, they are called "cv-qualifiers", and you can cv-qualify methods and overload them using the qualifier, which is a big part of why const is so useful in C++ : you can basically write const methods that "only read" the object, and non-const methods that "might write" to the object, and have transitive logical constness everywhere, but in special cases where you don't want it, that's easy too). Anyways, in some ways "const int" and "int" will be treated as the same type, but in other ways they won't.
And then there's a few special cases like "const int X = 5" where the language says the actual X variable can be elided and all uses of it replaced with the value 5. But for "const float Y = 5.0f", I think its allowed to replace uses with 5.0f but it can't elide the variable Y. Or something. I guess the moral of the story is: const is a weird animal !
Anyway, I'm beating a dead horse. tl;dr : const is really for humans, not compilers.
Back on topic...
hgm wrote:int * const nPcSq = &evalTerm;
Then the address is constant, but the evalTerm data can still be changed by learning, or when switching to a different variant.
The address is "constant" in the sense that you aren't allowed to assign a new address to the nPcSq variable, but that has nothing to do with whether it will be "a fixed address" at the spot where you use it
If and only if
evalTerm is a global variable of an array type (NOT pointer type!) then &evalTerm will be a "fixed address" known to the linker. If its anything else--function parameter, array variable on the stack, etc. then it won't be. Whether or not you mark it const, if and only if your compiler knows nPcSq points at a "fixed address" at the point where you use it, will it be able to generate instructions that access directly at that address, instead of indirectly through some register/pointer. (It doesn't need to know the actual address, just that it will be fixed once the program is loaded into memory.. the linker, and perhaps the loader, will help resolve the actual value)