The page follows on from the languages page, which reviewed basic concepts of the principles of how programming languages work, by further exploring how ‘variables’ programming languages work. Understanding this, helps understand the philosophies of python and kotlin.
In this page, the language ‘c’ is discussed, but there is no need to know how to program in ‘c’ any more than there was a need to program in machine code or assembler for the last chapter. It is just that explaining how ‘c’ works allows explaining some simple examples.
Variables and Static memory.
In a language such as the original ‘C’ language, a byte variable requires a single byte of memory to be reserved for that variable. The original ‘int’ variable required 2 bytes, and a string required one byte for each character plus one extra byte to mark the end of the string. If a string was to hold a variable number of characters, then the variable could be declared (as in ‘char name;’ to declare a variable called name able to hold 80 char-acters) with the maximum allowed size, and then either a separate variable declared to record the current length of ‘name’, or a special character at the end to indicate the end of the characters in use.
A new concept added in ‘c’ was the ‘pointer variable’. This variable would not hold program information such as a ‘char’ or an ‘int’ or a string, but would hold the number of the memory location (the ‘address’) where information is stored. Consider two ‘int’ variables ‘a’ and ‘b’ and a pointer variable ‘p’. The variable ‘p’ could hold the address of ‘a’ or ‘b’. Operations could operate on ‘p’, or they could operate on the memory location pointed to by ‘p’. This allows the same code to using ‘p’ to read or change either ‘a’ or ‘b’ depending on where ‘p’ is ‘pointing’ at the time.
All of these variables, including the pointer type, require a fixed amount of memory so the compiler could calculate the size of the block of memory to hold all the variables. These variable could be called ‘static’ variables as they use the same place in memory throughout the program. Every time a variable is declared, the space needed to hold that variable is reserved by the language.
A program with only static variables only requires a very low powered computer chip, and is the program in your toaster is very likely to work with only static variables.
The Stack and Stack Frames.
The Stack is a reserved area of static memory, with a ‘stack frame’ or new block of the stack allocated on the stack for each function on entry to that function and removed from the stack as the function completes and returns.
Local variables are allocated within the stack frame, and follow the exact same rules as other static variables.
Variables and dynamic memory: malloc().
Now consider the more complex problem of a linked list, with a variable number of elements. We could simply declare a variable with the maximum possible size for the most extreme circumstance for the program. This ‘maximum ever’ size would likely almost never eventuate, meaning that normally there a lot of wasted memory.
In ‘c’ the answer can come from a the ‘malloc’ function, which manages a pool of memory space, and allows allocating space from that came from a function q’ (memory allocate) where the program could ask the operating system for a block of memory of a given size at run time. The memory could then vary according to requirements. When the memory is allocated, the location of the memory could be stored in a ‘pointer variable’ and the program do whatever it wishes with the pointer. This for the first time allows true variable length variables. If we are finished with our list we can call ‘free’ to allow the space to be reused for any other data. We could later call malloc again asking for a different size, and this time our data may be in a different place. This makes our data ‘dynamic’ as where in memory our data is located, and how much space the data uses can change.
From the diagram, it can be seen that our static ‘int’ variable ‘a’ is held in static memory, but the dynamic ‘int’ variable ‘b’ is actually held in dynamic memory, so getting the value is a two step process. First get the pointer, then use the pointer to get the value. Dynamic variables make the program run slower, but they give greater flexibility.
Now consider objects. Objects are instanced at run time, so the easiest way to handle them is by simply holding a ‘pointer’ in static memory and creating the object in dynamic memory and then pointing to the object. A simple test for this in python is to try this code:
>>> c = [ 1, 2, 3 ] >>> d = c >>> c = 4 >>> print(d) [1, 4, 3]
It become clear that like that wonderful hand drawn diagram, both ‘c’ and ‘d’ share the same memory, so changing ‘c’ also changes ‘d’.
Every variable in python is in fact a ‘pointer’ or ‘reference’ to an object in dynamic memory. No exceptions. You may wonder “but if i set ‘a = 3’ and ‘b=a’ then add ‘2’ to a ‘a = a + 2’, then ‘b’ does not change!”. This is because ‘a = a + 2’ creates a new dynamic memory value ‘5’ and then ‘a’ is set to point to this new value, while b still points to ‘3’.
More on this in ‘basics’.