Learning the C Programming Language as a Classical Musician

Bonus Episode (after Ep. 15) — Recap of what we have learned so far

Welcome back!

More than one month has passed since my last episode. I have been unable to study, practice, and write anything concerning programming due to impending assignments in my music notation work. If you follow this blog you will have noticed how I have been far from silent, yet, I could not continue this series with the same regularity.

Now, before moving on with Episode 16 on C’s structures, I want to take a look back at what we learned so far, as it will help us catch up and get ready for the next step.

Let’s go!

Episode 1: Xcode p1

In the first episode of this series, we looked at the tools we would be using throughout our journey: a Mac, and Xcode. In it, most would be done in a Command Line Tool, selecting C as its language. I have been storing all the projects of this learning path here on GitHub, feel free to access them.

Episode 2: Xcode p2

In the second episode, we looked at Xcode’s UI: Navigators to the left, Inspectors to the right, and the Editor area in the center. A Debug area will expand from below to show our program’s output.

We learned that a comment is something that the IDE will not run and is introduced (in its basic form) by a double forward slash // (or shortcut Cmd-Shift-7.

At the top of the file, we usually have one or more #include to import functionalities into the program from files we create, or that macOS gives us. Then, the main function is what governs the whole program. We gave a passing glance at what a function is, creating an example to change a simple time into a compound time (e.g., 2/4 into 6/8). An important takeaway is that parameters are in the function definition, while they are called arguments when a function is called.

Episode 3: a C program

In the third episode, we dived into what a C program is. It is a list of declarations, made up of one of more identifiers (lines of code). Each of these have specifiers/qualifiers, declarators/initializers, and, optionally, an attribute specific sequence. The greatest takeaway here is that declarators/initialisers are just the names of objects. For example, my first name, Michele, is my declarator, my initialiser! If you think of it, when you are born, you are “initialised” with a name!

We introduced the concept of object-oriented programming, and that every object has a size, a duration and/or lifetime, and a value. Finally, we dipped our toes into the concept of scope, possibly one of the most important things to grasp at this stage.

Episode 4: comments

The fourth episode was dedicated to comments, both C++ style, introduced by //, and C style, introduce and closed by /* */. Xcode has some special comments, such as: // MARK: - Section name, // FIXME: -, // TODO: -, and #warning:.

We then touched the surface of the ASCII standard, seeing how text is actually represented by numbers (or our computers would not be able to tell it apart). Finally, we met the %c conversion specifiers, variadic functions (functions that can accept an indefinite number > 1 of arguments), the ternary conditional operator (i % 2 == 0 ? Y : N), and the modulo operator (a % b).

Episode 5: phases of translation

This episode was tough, in which we looked at the phases of translation, that is what happens when we compile our code. The most interesting phase to me is Phase 4, the preprocessor stage, where all files are reduced to single symbols that contain all that rich meaning at their inside!

Episode 6: identifiers & scope

In the sixth episode we delved deeply into identifiers, how objects are called, and scope, the context in which what we are using is accessible.

Episode 7: lifetime

In episode seven, we finally discussed lifetime, or how objects persist in memory before getting destroyed (recycled!). The concept of memory allocation is paramount here! We learned that 1 byte is made of 8 bits, and that an integer is 4 bytes long (usually!).

Takeaways:

  • Objects have a fixed lifetime, and ordinarily this is predefined by the programming language itself
  • 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.

Episode 8: namespaces

Episode eight introduced to us the concept of namespaces, a fancy term for object categories in C. There are six of them: label (case: or default: in a switch statement), tag (struct for structures, union for unions, and enum for enumerations), member (that is, inside the same object), attribute (which will become standard not before 2023), which is made of two possible specifiers [[...]] and ::, and the global namespace which collects everything else.

Episode 9: types

In episode nine, we started the long and ongoing series on C types, that is, the kinds of objects we can encounter in our code. Types are classified into four branches: void, basic types, enumerated types, and derived types.

Basic types are dividid into characters, integers (signed or unsigned), and floating-point types. Enumerated types are just enums. Derived types are arrays, structures, unions, functions, pointers, and atomic types.

Episode 10: compatible & composite types

In this episode, we looked at compatible and composite types. In short, compatible types are either the same type or very close to that, while composite types are created from two or more compatible types. It was a more theoretical than practical lesson.

Episode 11: Boolean, logical operators & characters

In Episode 11, we finally left theory behind to delve into practical example, starting with arithmetic types. First, a Boolean type is the computer representation of the true and false value. Then, logical operators are fundamentals to make code work afterwards:

  • The logical and operator is written as two ampersand characters, one next to the other && and returns true if both the statement to its left and the one to its right evaluate as true.
  • The logical or operator is written as two pipe characters ||. This expression will evaluate to true if either of the two statements around it is true.

Another important thing is the meaning of the single equal sign =, called the assignment operator, as it assigns the value to the right of it to the object position to the left of it, effectively storing it inside the object.

Finally, we faced characters and the fact of them being just numbers disguised.

Episode 12: Integers, conversion specifiers, and non-decimal numbers

In Episode 12, we continued our tour of arithmetic types, looking at integers, finally with some examples. My favourite part of this lesson was the conversion exercise between decimal and binary, octal, and hexadecimal!

Episode 13: floating-point types

This episode was completely devoted to floating-point (or fractional) numbers and to their application in C. I did not have the courage—nor the knowledge—to venture into complex and imaginary numbers, as it is really is not my field.

Episode 14: enumerated types

Episode 14 made us plunge into the world of enumerated types, that is, a common ground for a group of related values that will be stored as integers. We finally had the possibility of relating this type to the musical language, as notes are just a list of “related values” ordered according to a convention.

Episode 15: arrays

In this episode, the last of our review before finally progressing into uncharted territory, we attacked derived types, starting with arrays. An array is made of a sequence of objects of the same type, doesn’t change size, and starts counting from 0. A most fascinating aspect of arrays in C is how one can declare their size in multiple ways, using integers, sizeof() functions, text strings. They are so powerful!

Conversion specifiers

Before moving on to the next episode, I want to compile a list of conversion specifiers, as this is possibly the thing I have spent the most time on when looking for information. Excerpts from the official C documentation are included beside each example. Extremely difficult parts are added in footnotes for completeness.

  • %c: character. Supports char and unsigned char. Writes a single character. The argument is first converted to unsigned char1.
  • %s: string. Supports char *. Writes a character string. The argument must be a pointer to the initial element of an array of characters. Precision specifies the maximum number of bytes to be written. If Precision is not specified, writes every byte up to and not including the first null terminator2.
  • %d or %i: signed integer. Supports short, unsigned short, int, long. Converts a signed integer into decimal representation -dddd3.
  • %o: octal representation of integer. Supports short, unsigned short, int, unsigned int, long. Converts an unsigned integer into octal representation oooo4.
  • %x or %X: hexadecimal representation of unsigned integer. Supports short, unsigned short, int, unsigned int, long. Converts an unsigned integer into hexadecimal representation hhhh. For the x conversion, letters abcdef are used. For the X conversion, letters ABCDEF are used5.
  • %u, %lu, %hu, %llu. Unsigned integer. Supports, respectively unsigned int and unsigned long, unsigned int and unsigned long, unsigned short, unsigned long long6.
  • %f: floating point. Supports float. Converts floating-point number to the decimal notation in the style -ddd.ddd7.
  • %e or %E (also possible as %g or %G): scientific notation of float values. Supports float and double. Converts floating-point number to the decimal exponent notation. For the e conversion style -d.ddde±dd is used. For the E conversion style -d.dddE±dd is used8.
  • %g or %G: another possibility for scientific notation of float values9.
  • %a or %A: converts floating-point number to the hexadecimal exponent notation. For the a conversion style -0xh.hhhp±d is used. For the A conversion style -0Xh.hhhP±d is used10.
  • %hi: signed integer (short). Supports short.
  • %l, %ld, or %li: signed integer. Supports long.
  • %lf: floating point. Supports double.
  • %Lf (notice the uppercase L): floating point. Supports long double.
  • %lli or %lld: signed integer. Supports long long.
  • %p: address of pointer. Writes an implementation defined character sequence defining a pointer.
  • %n: returns the number of characters written so far by this call to the function. The result is written to the value pointed to by the argument. The specification may not contain any flag, field width, or precision.

What’s next?

In the next episode, we will look at the next derived type in the series: structures! See you then!

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.

  1. If the l modifier is used, the argument is first converted to a character string as if by %ls with a wchar_t[2] argument.
  2. If the l specifier is used, the argument must be a pointer to the initial element of an array of wchar_t, which is converted to char array as if by a call to wcrtomb with zero-initialized conversion state.
  3. Precision specifies the minimum number of digits to appear. The default precision is 1.
    If both the converted value and the precision are ​0​ the conversion results in no characters.
  4. Precision specifies the minimum number of digits to appear. The default precision is 1. If both the converted value and the precision are ​0​ the conversion results in no characters. In the alternative implementation precision is increased if necessary, to write one leading zero. In that case if both the converted value and the precision are ​0​, single ​0​ is written.
  5. Precision specifies the minimum number of digits to appear. The default precision is 1. If both the converted value and the precision are ​0​ the conversion results in no characters. In the alternative implementation 0x or 0X is prefixed to results if the converted value is nonzero.
  6. Converts an unsigned integer into decimal representation dddd.
    Precision specifies the minimum number of digits to appear. The default precision is 1. If both the converted value and the precision are ​0​ the conversion results in no characters.
  7. Precision specifies the exact number of digits to appear after the decimal point character. The default precision is 6. In the alternative implementation decimal point character is written even if no digits follow it.
  8. The exponent contains at least two digits, more digits are used only if necessary. If the value is ​0​, the exponent is also ​0​. Precision specifies the exact number of digits to appear after the decimal point character. The default precision is 6. In the alternative implementation decimal point character is written even if no digits follow it.
  9. Converts floating-point number to decimal or decimal exponent notation depending on the value and the precision.
    For the g conversion style conversion with style e or f will be performed.
    For the G conversion style conversion with style E or F will be performed.
    Let P equal the precision if nonzero, 6 if the precision is not specified, or 1 if the precision is ​0​. Then, if a conversion with style E would have an exponent of X:
    – if P > X ≥ −4, the conversion is with style f or F and precision P − 1 − X.
    Otherwise, the conversion is with style e or E and precision P − 1.
    Unless alternative representation is requested the trailing zeros are removed, also the decimal point character is removed if no fractional part is left.
  10. The first hexadecimal digit is not 0 if the argument is a normalised floating point value. If the value is ​0​, the exponent is also ​0​. Precision specifies the exact number of digits to appear after the hexadecimal point character. The default precision is sufficient for exact representation of the value. In the alternative implementation decimal point character is written even if no digits follow it.

Published by Michele Galvagno

Professional Musical Scores Designer and Engraver Graduated Classical Musician (cello) and Teacher Tech Enthusiast and Apprentice iOS / macOS Developer Grafico di Partiture Musicali Professionista Musicista classico diplomato (violoncello) ed insegnante Appassionato di tecnologia ed apprendista Sviluppatore iOS / macOS

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: