In this article, we’re going to be covering (a part of) the very basics of the C Programming Language and how you can start developing your own software written in C, whether that be to execute on macOS, or even your iPhone!
So, before we get started with the code itself, there are a couple of concepts to understand.
The code you run is not your source code (not directly, anyway). - Of course, what you write in your source code will determine the output, but your source code has to go through the process of being compiled.
This is a complex background process we don’t have to understand fully, but it’s always positive to know, even if it’s just a case of being aware of it, so here it goes - the compilation process of a C Program:
- The C source code, after being pre-processed, becomes a translation-unit
- The compiler will inspect the translation-units for errors, and if none are found, generate an object file which is the machine code representation of our source code.
- The linker will generate a single executable file we’ll execute, from the multiple object files
With the introduction out of the way, we’re ready to write our first C executable.
I'd personally recommend using CLion from JetBrains. It’s a fantastic IDE for beginners, as you simply hit the play button and your source code is compiled and executed all from the same window!
This is not a paid endorsement and is 100% my own opinion. CLion has a 30 day evaluation license and is completely free for students and for use in Open Source projects.
We’ll opt to create a new C Executable with the C99 standard.
Your window should now have all the jazz of an IDE surrounding the default project.
To begin, let’s establish an aim for what we’d like to make for our first C executable. I’d generally start by suggesting we make an application to count Apples and Oranges, although I feel that won’t be too suitable for an iOS Exploration course, weirdly…
We’ll work through each step to create the ‘ios’ tool I mentioned earlier, to help us quickly execute code on our iOS Device. It’ll be quick and easy to use, and we’ll even add it to our PATH, a directory our Operating System looks for binaries within, so we don’t have to cd to the directory of our binary each time to use it!
We can begin by removing the ‘hello world’ printf statement.
- printf is a function that takes in some text as an argument and prints it to the output of our terminal (stdout).
- We don’t need to define how printf works because we have used #include <stdio.h> at the beginning of our C document.
- Adding the #include allows us to include functions from other ‘libraries’. Libraries are collections of functions.
- stdio.h is a library.
Our ‘main’ function is where the program begins executing. We need to add ‘int argc, char *argv’ to the data our main function will take in so that we’re able to process the arguments passed to our application.
I’d like to quickly add that an int represents a whole number ranging from a possible size of -32,768 to 32,767 .
Hit the play button at the top right of your display, and observe the output the appears at the bottom area of your display. You should see:
Process finished with exit code 0
If we look at the code. This makes logical sense, as the program begins, and simply returns the value 0.
- A return value of 0 means success. Anything other than 0 is an error of some sort. The great thing about C is we define the errors and where they happen. So we can fix them!
With that in mind, let’s add a little bit of validation because we want to make sure the user has entered ‘something’ for our tool to execute on the iOS Device.
We’ll make an if statement as shown. argv is the first argument passed to the program, so if that’s 0, how could there possibly be any data being sent to the program?
We use the double equals ‘==‘ to compare two values. We’re comparing the first argument against the value 0, and so we’re checking if it’s empty.
We then use the curly brace (which should automatically create a closing curly brace) and use the printf function we saw earlier, to print an error message for our user to see.
We can then also make use of return values, returning 1, which represents an error, because nothing was passed to the software and therefore cannot fulfil its purpose.
After the closing curly brace of our initial error check, let’s create the variable ‘command’. We’re statically allocating 1200 bytes to this variable, which is far more than we’ll need.
A string in C, is stored as an ‘array of characters’ or a ‘char array’.
We do this is because the memory use of our C program is completely up to us to manage properly. We could run over the edge of the allocated and enter 3000 bytes worth, but we’d then be writing into the memory in use by the application which will inevitably cause a crash.
As an extension once we’re done, try a small value for the char array size, and see what happens - then try to work out why and what’s happening in the background.
I’ll now introduce to you, sprintf.
sprintf is another function from the standard C library, and allow us to parse multiple datatypes and output our formatted output to a char array.
Reproduce the code as shown here. We can see that we’re initialising the command we’re going to execute. As of yet, there’s nothing to handle the user input…
We’re now using a ‘for’ loop. A for loop will iterate through a set of values. We firstly have to initialise our for loop with a ‘counter’. The most common initialiser here is int i=0. We’re going to initialise our for loop with i=1 and I’ll explain why in a few moments.
We use the ‘;’ to define the end of a statement. Our statement is int i=1 so we’re going to close that statement using ; and begin our ‘condition’ for the for loop to carry on executing.
- The condition in our case is executing until we’ve iterated through everything the user has inputted, so our command char array is then ready for us to execute!
- We then close our statement and begin our ‘action’ which results in the counter changing in some way. Our action here is i++ which is essentially i=i+1. In other words, each time the loop executes, we move the counter up by one.
- Within this for loop, we use sprintf once more, replacing the value of command each time.
- We use the format identifier ‘%s’ to treat the input as a string.
- The great part of for loops is we can use the counter within the loop.
The for loop here replaces the value of command with itself, plus a blank space ‘ ‘ followed by the contents of argv[i].
The value of i will change as the for loop executes, as its running through the command line arguments we’re entering and adding it to the command char array. Make sure to close the for loop using the closing curly brace ‘}’!
That’s a bit of a mouthful, and I hope that sort of made sense. When you continue experimenting with the code, it’ll become more and more natural.
There’s just one (or two) more steps. We have to actually execute that final command.
In this case, we can do that using a simple call to the system function.
In the future, we should implement popen for executing commands, but as this is a very introductory tutorial, that won’t be necessary for now.
The command we want to execute is held within the char array command, so we’ll pass that to the system function.
system isn’t actually included in the default library of functions. It is however included in another standard library, stdlib.h
See if you can include the stdlib.h library yourself without reading ahead.
At the top of our C document, add the following line below our first include:
Behold, our first, fully working (unless a typo took place) useful C binary - congratulations!
If you browse to the project folder, you’ll see a folder called ‘cmake-build-debug’. Our binary is held within there and is named the same as our project by default.
Behold, our final code (don’t forget to hit play to compile!)
One last thing…
We need this process to be complete - and we can only do that one way. Adding the binary to our PATH. The PATH holds folders to look for binaries, where we aren’t working from the folder of the binary. For example /usr/bin is in PATH and holds many binaries you’ll likely use, like grep, cat and others.
Use the following instructions to make a bin directory in your home folder and add the folder to your path temporarily… (you’ll need to execute these from your terminal prompt)
- mkdir ~/bin
- export PATH=$PATH:~/bin
Easy peasy. Now move the binary from the cmake-build-debug folder to the bin folder in your home folder, and you’re ready to go! Amazing!