Episode 7
Welcome back!
In the last episode, I had promised you that we would have looked together at the concept of lifetime in C, but the review of identifiers and scope took much more time than expected. Today we are here to rectify that, and possibly something else.
Let’s get started.
Lifetime
Basically every day we are surrounded by news, articles, TV shows, and more talking about ecology, environment, recycling. It is simple: by the sheer act of living we produce waste, an enormous, almost endless amount of waste. This waste needs to go somewhere as, sadly, it cannot just disappear after being produced; it needs to be collected, classified, separated, and, where possible, recycled. Every object, thus, of every origin, has a start of life—its production—and an end of life—its disposal. It is a cycle which encompasses all lifeforms, but is it so important for programming as well?
While this is still true in the modern computer, there was a time when calculators were as big as rooms, when hard drives were the size of your washing machine, all the while holding a few megabytes (MB) of data in capacity. The memory that makes what a computer does possible, called Random Access Memory (RAM), was just a few kilobytes (kB) big and so anything mismanaged was a potential problem, a looming crash. This is logical because a computer works by storing temporary data in RAM, working with it for as long as it’s needed, then in either transferring it to long-lasting storage (the hard disk drive) or destroying it.
Modern programming languages such as Swift have become very efficient in dealing with potential memory leaks, and have a robust series of tools to either avoid them altogether, or to easily find the culprit and fix it. At the time of C, and by extension, even modern C++ code, this was much more complicated, and it required that every object created would have a lifetime, that is, a limited and predictable amount of time during which the object would have persisted in memory.
I want to indulge a few moments on this: in our modern world, we too often think of computers, mobile phones, tablets, as supernatural devices, where everything behind that flashy screen is ethereal and out of our grasps. That couldn’t be farther from the truth! Even if our modern devices are fiendishly fast and efficient, they still physically write data to a precise address in memory, and that data is physically erased from those transistors when it is no longer needed. An array of four characters, for example, occupies 4 bits, and not some random ones: the first character occupies a precise bit, with a specific address, and the next three characters occupy the next three bits in a row. Imagine a game of battleships, where once you find the tip of a target, you just need to proceed in line until it is destroyed.
In music, this concept could be applied to an enormity of subjects: a note has a lifetime, in that it rises from the first contact of the finger on the piano key and falls when the damper stops the string’s vibration. A printed score has several lifetimes: its lifetime could be related to its use, from when the conductor opens it for the concert to when the last page is closed and the book is brought back to the library. Or it could be its objective lifetime, from getting out of the printer to its recycling, though I sincerely hope you will never throw away a musical score. If you ever feel like doing so, contact me and let’s talk about it! In cello playing, even single bowing strokes have their lifetime, even if we, the players, have to do our best to make the change between one stroke and the next inaudible.
In programming, there is a fundamental word that needs to be memorised when talking about memory management: allocation. When an object is created, we need to allocate enough memory for it to physically exist in the computer. Only then we can use it and, later, destroy it. Luckily, C being a middle-level language1, many of these operations are already considered by the language itself. Scope also plays an important role and I hope you have retained something of what we addressed in the previous episode, as according to different scopes, objects may or may not be available for you to use.
A scientific definition of lifetime in C is that every object, in C, exists (that is, is accessible to us), is given, and has knowledge of a constant address in memory, and keeps unaltered its most recently stored value, over a specified portion of the program’s execution. For example, if we have a basic function that takes two integers and returns their sum:
int sum(int x, int y) {
int z = x + y;
return z;
}
I know, we could have just written return x + y;
but I wanted to create an extra object to show you its lifetime. The objects x
and y
exist from the moment we call this function, and are destroyed right after the return
line or, if possible, even after the creation of the z
object. I tell you now, but we will investigate it more later, that an integer is 4 bytes big (or long), so when the compiler creates an integer, it must allocate 4 contiguous bytes of memory to store it. It is fascinating that when I write the identifier of the function int sum(int x, int y)
, basically no memory is expended (apart from the one used by the IDE to show this on screen for us, but since they are all characters, we are in the realm of bits, 1/8 of a byte). When the function is called, 12 bytes are allocated: 4 for the return integer that we know we will need, and 8 for the two parameters. Inside the function we get to allocate 4 more bytes for the creation of the z
variable, we store inside it the values of the two parameters, and then return it, copying the value from z
to the pre-allocated return integer. Right after this copying is done, the compiler can free the memory used for z
and for the two parameters, but obviously this is less efficient than just writing return x + y;
in which we have a total of 12 bytes allocated, and 8 freed (or deallocated) after the return
line. It may appear that 16 bytes against 12 would be too small of a difference to really matter, but think for a moment: 1/3 more memory for such a basic function, what would happen if I had the same waste in a much bigger function? The program would run slower, the computer would possibly overheat, and the performance overall would suffer.
Of course, I am no computer scientist, I am just a musician, and experts will probably criticise my over-simplistic and potentially incorrect approach, but my goal here is to pass a concept, an idea, and then base our future accomplishments on having grasped that concept.
When C wants to tell you that you cannot do something, it tells it with a scary couplet: it is undefined behaviour. After calling our mighty sum
function, for example, like so:
int main(int argc, const char * argv[]) {
printf("The sum of 3 and 2 is %d\n", sum(3, 2));
return 0;
}
We could try to use z
, thinking that it would still be around. To our surprise, we would get this error:

“Use of undeclared identifier ‘z’”—now we all know what an identifier is, and what a declaration is. What the IDE is telling us is that we have not created any object named z
. But wait, we would ask, I have created it inside the sum
function. Yes, true, but that object has been destroyed, according to the rules on scope that we delved into in the previous episode.
When I was first studying Swift, with no prior programming knowledge, I was hitting my head against the wall every time I was seeing an error in Xcode because I had absolutely no idea what that would mean. Now I do not need to panic anymore, as I know, or have the basic knowledge to try to understand, what those words mean. At the time, Xcode would have needed to write: In the current scope there is no object called z
, while now, this error is totally understandable.
To conclude this episode: objects have fixed lifetime, and ordinarily this is predefined by the programming language itself, which also gives certain specific objects a temporary lifetime to avoid further problems. Typically, we do not have to worry about allocating and deallocating memory, but we must be aware of objects existing in a precise time and space frame.
What’s next
I am delighted I could dedicate a whole episode on objects’ lifetime in C, I am convinced it was worth the wait.
In the next episode, we will try to understand more of how the C programming language interprets and classifies the objects it encounters.
Bottom Line
Thank you for reading today’s article.
If you have any question or suggestion, please leave a comment below or contact me using the dedicated contact form. Assuming you do not already do so, please subscribe to my newsletter on Gumroad, to receive exclusive discounts and free products.
I hope you found this article helpful, if you did, please like it and share it with your friends and peers. Don’t forget to follow me on this blog and to let me know what you think.
If you are interested in my music engraving services and publications don’t forget to visit my Facebook page and the pages where I publish my scores (Gumroad, SheetMusicPlus, ScoreExchange and on Apple Books).
You can also support me by buying Paul Hudson’s Swift programming books from this Affiliate Link or BigMountainStudio’s books from this Affiliate Link.
Thank you so much for reading!
Until the next one, this is Michele, the Music Designer.
- A low-level language is one which is nearer to the machine, for example Assembly, while a high-level language is one that deals with things with a good deal of abstraction, such as Swift. ↩
One thought on “Learning the C Programming Language as a Classical Musician”