Skip to content

Instantly share code, notes, and snippets.

@remzmike
Last active March 29, 2023 00:44
Show Gist options
  • Save remzmike/f02c6ffa84ab679ca4b738d4ccf9bc67 to your computer and use it in GitHub Desktop.
Save remzmike/f02c6ffa84ab679ca4b738d4ccf9bc67 to your computer and use it in GitHub Desktop.
Hello C : 02 : Header files and library functions

Part 2: Header files and library functions

About Header Files

In part 1, we had this line at the top of 1.c:

#include <stdio.h>

Then we called this standard C function:

printf("Hello, World!");

The compiler will not compile a call to a function unless it knows the function.

The compiler knows the printf function because of the #include line.

How does include work?

Before C compiles a file it gets modified by the C preprocessor, which has a separate syntax.

The syntax of the C preprocessor mostly involves directives which start with a #.

The include directive replaces the line with the contents of the referenced file.

This allows you to share declarations of functions so they can be called in other files.

The .h files are called header files, even though they are still C code.

In the standard C runtime library the printf function is said to be declared in stdio.h header, so we know we have to include that file if we want to compile a call to printf.

Calling a Windows function

In part 1 we learned that KERNEL32.DLL contains the core Windows API.

We saw that one of the functions exported from it is Beep. If we want to use that function we need to know how to call it, and we need to tell the build how to find it.

Google: win32 beep

https://learn.microsoft.com/en-us/windows/win32/api/utilapiset/nf-utilapiset-beep

The documentation tells us how to call it. This is the function signature.

    BOOL Beep(
        [in] DWORD dwFreq,
        [in] DWORD dwDuration
    );

That basically says the function takes two integer parameters.

It also shows an example of how to call the function.

    Beep( 750, 300 );

Create 2.c and copy in this code.

#include <stdio.h>
#include <Windows.h>

void main()
{
    printf("Hello, World!");
    Beep(750, 300);
}

I added an include for Windows.h because that is where Beep is declared.

Looking back at the Microsoft documentation, near the bottom, in the Requirements section:

Header: utilapiset.h (include Windows.h)  

The documentation also tells us what lib file it is in.

Library: Kernel32.lib  

The linker needs Kernel32.lib. However, this specific library is linked automatically since it is a core Windows library.

So, we can compile just fine right now with:

> cl 2.c

Which is the same as linking with Kernel32.lib explicitly.

> cl 2.c Kernel32.lib

That commandline is a little weird though, mixing c and lib files, which are parts of two different steps in the build.

The compiler documentation tells us what is going on:

"CL [option...] file... [option | file]... [lib...] [@command-file] [/link link-opt...]"

https://learn.microsoft.com/en-us/cpp/build/reference/compiler-command-line-syntax?view=msvc-170

In cl 2.c Kernel32.lib, 2.c is the file... part and Kernel32.lib is the [lib...] part.

The above command is equivalent to these two:

> cl /c 2.c
> link 2.obj Kernel32.lib /out:2.exe

The key concepts to remember are:

  • You need the header to compile, and the library to link
  • Use header files to tell the compiler about other functions
  • Link with the .obj and .lib files containing the actual code

A simple beep song

Let's use the Beep function, and another Windows function, Sleep, to play a simple song.

Copy this code into 2.c and save:

#include <stdio.h>
#include <Windows.h>

void main()
{
    Beep(330, 127);
    Beep(330, 275);
    Beep(330, 137);
    Sleep(137);
    Beep(262, 137);
    Beep(330, 275);
    Beep(392, 550);
    Sleep(550);
    Beep(262, 412); 
    Beep(196, 137);
    Sleep(275);
    Beep(164, 137);
    Sleep(137);
    Beep(220, 275);
    Beep(247, 137);
    Sleep(137);
    Beep(233, 137);
    Beep(220, 275);
}

This will play through the System Sounds channel, so you might need to unmute that in the Windows volume mixer.

Check your volume, then compile and run:

> cl 2.c & 2

You should hear the start of the mario theme.

How code executes

When the above program is executed, the whole exe is loaded and the OS calls main.

At the fundamental level, you can think of program execution as being performed by a single cursor moving around in your program, while other running programs have their own such cursors, and the OS keeps track of them.

When the first Beep line is encountered, the cursor goes into the Beep function body, and when that function body ends, or returns, the cursor comes back to main just after where it was before going into the Beep function body. When you use a debugger to step through code this will be a lot more clear, but this description is good enough for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment