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.
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.
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
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.
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.