A JavaScript Developer’s Guide to C (Part I)

Posted by
Photo by Zach Lucero on Unsplash

The author of this story is Josiah Savary, and it was originally published on his blog. Josiah is an engineer at Parsec primarily focusing on the JavaScript front-end application, but most of the core application at Parsec is written in C, so he spent some time exploring the language recently for a hackathon project.

There are many new languages with native build targets: Swift, Rust, ReasonML, etc. But let’s take a look at an often overlooked choice: C.

Indeed, there is a lot to learn about when it comes to C. But as it turns out, if you know JavaScript, C has a very approachable syntax. It’s really fast and can tap into most functionality available on any given operating system. Plus, it can run pretty much anywhere. In fact, it’s super easy to create bindings that allow us to execute C functions from Node.js or, thanks to WebAssembly, even directly from a web browser.

Hopefully, if you’re anything like me, getting a little C under your belt will not only be a great addition to your skillset — it’ll also feel like a personal victory. Let’s get started by learning how to compile and run 3 variations of, the classic, “Hello, World!” 🎉.


I GCC What You Did There

To compile C code, we’re going to need a compiler. Luckily, C is one of the languages supported by the GNU Compiler Collection (GCC), and chances are it’s already available on your system. If so, we can use it on the command line via gcc.

Note: If you’re on Windows, you probably don’t have gcc available in Command Prompt 😭. You will need something like MinGW to follow along with the rest of this post.

Working with a Single File

Let’s create a hello.c file and add the following code to it:

https://gist.github.com/jozanza/63dd5f9d6a218638b89f045adfa597dc.js

The absolute simplest way to compile it is with the following command:

gcc hello.c

By default, gcc will produce an executable file named a.out¯_(ツ)_/¯. We can run the file like so: ./a.out. Maybe it’s just me, but I usually want to name my program something else, so I often do something like this:

gcc hello.c -o hello

________

Cool. The -o flag lets us name our executable. Similarly, we can run it with ./hello. But what if we have multiple source files? 🤔

Working with Multiple Files

Now, we’ve got hello.c just like before, but we’ve added a greet.h and greet.c. Files that end in .h are called “headers”. It’s their job to declare the public type definitions their respective .c file will implement. Examine the following:

https://gist.github.com/jozanza/f9d930f3746ebef8b419561147f34e4f

A little complex for a “hello world” program, but I’m just trying to get a point across 😛

First, we must make sure to add the necessary #include statements. Then to compile, we simply list out all of the .c source files in use:

gcc greet.c hello.c -o hello

Not bad 😎.

Using Libraries

It’s often the case in application programming that we don’t want to write every single bit of code ourselves. Let’s say we want to make a game, for instance. Writing all of the cross-platform window management, input polling, and graphics rendering code from scratch will take us pretty much forever. So to make our lives easy, we decide to use Raylib.

We can install it to the standard location via the command line (brew install raylib on macOS, for example). Then, we write a few lines of code to open a window with our canonical greeting:

https://gist.github.com/jozanza/34708939893947cc672e9cd0b4e9bc10

The library we depend on doesn’t exist as a single file in our project directory, so simply adding raylib.c won’t work. Technically, it’s a .h header file and an archive file living somewhere on our system¹.

Anyways, this just means it’s time to use -l:

gcc -lraylib hello.c -o hello

Too easy. Under the hood, gcc uses a linker called ld to include libraries. The -l flag lets the linker know which ones to search for and include. If we had multiple libraries, we’d just list them out like -lfoo -lbar -lbaz. Anyways, we can run it with ./hello, just like in the previous examples, to pop open an application window with a familiar greeting 👋.


What’s Next?

There are many other useful and interesting compiler flags to learn about , but we’ll skip those for now². And as fun as it may or may not be to use gcc directly, we ought to figure out how to automate the build process.

In the next part, we’ll solve this problem by learning about, our good friend, the Makefile 💪.

¹ In C, libraries have two components: a header and an archive file. Archives come in static and dynamic flavors: .a.so on Linux, .a.dylib on MacOS, .lib.dll on Windows , respectively. By default, gcc will probably search for library headers in /usr/local/include and /usr/include and for archives in /usr/lib and usr/local/lib. However, this can vary depending on your setup. If we do happen to place a library header or archive somewhere the linker wouldn’t normally look, the compiler will fail unless we explicitly tell it where to look. So in addition to -l, we would also need to use the -I and -L flags: -I/path/to/header/dir and -L/path/to/library/dir.

² Just run gcc --help if you’re curious. For more in-depth explanations, check out the docs for Overall Options, C Dialect Options, and Options for Linking. Or for a simple overview, you can quickly browse the Option Summary. Keep in mind, many of these flags may be somewhat difficult to grok until you actually need to use them.

Thanks to Erik Nygren for reviewing.

Leave a Reply