"Constants" in programming

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
"Constants" in programming
by on (#116876)
Usually, it is common belief that there is two kinds of numerical values in computer programming, variables and constants.

I am confused at what a "constant" is, and I was most confused by the "const" keyword in C++ for insance. I can count many types of "constants" :

1) Universal constants like pi, e, etc... Those never changes and are the only true constants.

2) Compile-time constants. You decide their values when you compile your program, and never change this.

3) Mathematical constant. They are almost the same as compile time constant, except they are not defined directly but are the result of another calculation involving several constants of the first two mentioned kinds. Like if you insert (3*10 / 4) in your code.

4) Constant argument to function. It's when you call a function and one of your arguments is one of the already mentionned 3 types of constants. Like in "my_function(2);"

5) Local constants. They are affected at their creation (can also be an argument inside a function, which is a special case of a variable/constant being created then affected by the caller). They can be affected by variables making them take different values at execution time. However they are "constant" in the sense that you can't affect any value to them a second time.

As far I know there is 2 reasons to have constants in the 1st place :
1) Security - you don't want to accidentally affect a value to a "variable" whenre this shouldn't be allowed
2) Optimisation - knowing some value is constant allows the code to compile into something faster and smaller (for instance, if you know you multiply by 4, you avoid a MUL instruction and just preform a shift left by 2).

All those 5 types of "constants" can lead to various optimisations which are drastically different from each others :

1) In a HLL, since a constant is universal you could build it in the language directly, and avoid having the user define his own PIs and Es again and again. However this won't make any difference in compiled code

2) Any compile time constant does not have to be stored in variable RAM, it can just be hardcoded with the ROM or program RAM.

3) Any math involving more than one compile-time constant should be done by the compiler and not by the program ! Only exception : When it affects precision in integer math, as in 4*var/3 will not be the same as (4/3)*var = 1 * var !

4) This can optimise the way in which the function is called since there is no need to push a variable on the argument stack, but just a raw number

5) In the case of a function call with a "constant" argument, you could pass the argument by reference even if the user doesn't specify this, as it will never be modified anyway
In the case of a user declared "constant", it doesn't make much of a difference I think.

Apparently C used the const keyword for 5, but it can also mean 1, 2 and 3 but those have to be detected by the compiler in order to be optimized.
Re: "Constants" in programming
by on (#116880)
The order of operations also affects precision in floating point math (albeit more subtly), so the compiler is really never allowed to reorder operations when it could change the result of the expression.
Re: "Constants" in programming
by on (#116894)
Constants are named values that don't change during program execution, contrasted with variables which hold state and change. It's about the dynamic behavior.

In C and C++, there are two basic kinds of constants: const objects, and compile-time constants.

A const object is merely one which the programmer has marked const and for which the compiler will prevent accidental modification by giving an error during compilation if it's attempted. The programmer can circumvent this by using casts, though this can result in undefined behavior, e.g. when the object is placed in read-only memory. Const objects exist to express and enforce constraints that the programmer finds useful for catching bugs or making operation clearer.

A compile-time constant is a value that can be used where the compiler must know the value during compilation, like the size of an array in a struct. There are rules for what is a compile-time constant, for example that it must be a literal value or an integer declared const, so that it will be consistent across compilers regardless of how "smart" they are and what all they are capable of calculating at compile-time (a really smart compiler might be able to run your entire program during compilation and predict the result, for example).

There is a third type, the invisible constant where the compiler is able to determine the value when compiling the program, and can thus make optimizations of the kind you mention. Here making objects const can help, because combined with the rule that modification is undefined behavior, the compiler can know that it won't legitimately be modified anywhere and thus assume its value cannot change and make optimizations. In effect, const gives the compiler a larger view of the program and things it won't do while running , and optimizations are based on knowledge like this.
Re: "Constants" in programming
by on (#116997)
Yeah, the major confusion comes from the fact that "const" is used for a read-only variable while #define is used (among other things) for real constants.
Re: "Constants" in programming
by on (#116999)
Well, #define was traditionally used for constants, since it bypasses the need for the compiler to identify and apply constancy to immediate values. I think this has fallen out of favour, somewhat, though.

In general, I prefer to use a const int in a header to a #define, for a couple of reasons. Modern compilers are fully capable of optimizing them as constants. You can place them in scopes, and use all the other language features that you get with variables that you would be bypassing with #define (e.g. sizeof). Also, as always macros are a great source of frustrating compiler errors (usually the error looks totally unrelated to what the problem is).

There are situations where I would still use a #define for whatever reason, but they're not my preference for constants.
Re: "Constants" in programming
by on (#117074)
Yeah. Macros are often frowned upon, but they are sometimes very convenient.
So never listen to someone saying never do something.

Ooops.
Re: "Constants" in programming
by on (#117075)
Enum often works just as well: enum { foo = 1234 }; It's scoped so it won't conflict with local variables of the same name, etc.
Re: "Constants" in programming
by on (#117083)
Yep, and as they are actually symbols to the C compiler (and not text transformed by a preprocessor), you have access to enum values in debuggers.
Re: "Constants" in programming
by on (#117120)
Jarhmander wrote:
Yeah. Macros are often frowned upon, but they are sometimes very convenient.
So never listen to someone saying never do something.
I think macros are very good, although C macros aren't as powerful as some others, you can make many things with macros.

(I usually do use macros for constants, although this isn't the only way to make constants. Sometimes I use enum, which sometimes work better especially to auto-number them. There are other things can be done with macros, too, though.)
Re: "Constants" in programming
by on (#117126)
Bregalad wrote:
Yeah, the major confusion comes from the fact that "const" is used for a read-only variable while #define is used (among other things) for real constants.


This is an amazingly insightful observation.

There isn't anything more at fault for all the buffer overflow exploits than the mixup in C you just pointed out.

#define typically resolves into an immediate load CPU instruction if used literally. It does not 'define' anything because everytime it is used, it could be redefined by changing the payload data in the immediate instruction for that one only.

const typically leaves a memory location that something else can overwrite quite easily. This is seriously likely because although array size has to be declared with an exact known value, unfortunately then that value is completely forgotten about because C doesn't boundary check.

Time has shown this aspect of C is completely, utterly wrong.


...On a more metaphysical but harmless sense in tune with your observation, MOVE is an incorrectly titled instruction. It does not relocate a value from one place to another, it makes a COPY of it.
Re: "Constants" in programming
by on (#117138)
A compiler can and does treat the two interchangeably. If it sees a literal value used often, it might put it into a register/memory (if it takes more than one instruction to produce). And if it knows about a const object, it might cache it in a register if it's used a lot in one place. Security has little to do with it, because if someone can overwrite your program, you've already got a gaping wide hole.
Re: "Constants" in programming
by on (#117139)
whicker wrote:
const typically leaves a memory location that something else can overwrite quite easily. This is seriously likely because although array size has to be declared with an exact known value, unfortunately then that value is completely forgotten about because C doesn't boundary check.


Not really. On modern compilers a const int typically becomes an immediate value when optimizations are enabled. If you create a reference to it, the compiler is forced to put it in a memory location somewhere, but because it's const, it can be used both as an immediate value and a stored value in the same program (i.e. doing illegal things to change its stored value still won't affect the places it is immediate).

Furthermore, const data that does need to be stored can also be grouped with the code, which on some systems can be made read only to protect it from accidental or illegal overwrites.


The lack of boundary checking has the advantage that your code has less automatic overhead. In C++ it's fairly common practice to use array wrapper templates to add the boundary checking where needed. The default implementation of std::vector usually does bounds checking, but this can also be disabled. Additionally, there are many tools available to help inspect your code for bounds safety. Yes, none of this is built into C directly as a language feature, but it's available, and most importantly it's optional.