C++1: Headers, Debugging, and Directives

nubb
5 min readJun 28, 2024

--

Welcome back, Medium! School’s done for me and I’m going full speed on learning C++ (currently through learncpp.com). Today marks the first blog post on my C++ learning journey, which will be going over some beginner concepts I’ve learnt like function return types, variables, undefined behaviour, header files, the #include directive (and what preprocessor directives do), and debugging techniques!

(brought to you by the greatest repo ever :D)

int main()

Let’s first go over functions, variables, and undefined behaviour.

Compared to python, C++ is statically-typed, meaning there is no guess-work with variable or function types, thereby meaning the compiler has to know the return type and data type for things like functions and variables respectively, otherwise you’ll get compile errors.

Consider the following program:

#include <iostream> // include iostream file contents using preprocessor directive #include

// main function required by C++ language to run, otherwise nothing will happen
// returns integer type, either a literal (e.g. the number 4, an explicitly stated value) or a variable
int main()
{
std::cout << "Hello, Medium!"; // explicitly stating std (standard) namespace

return 0; // returns literal 0, notifying OS that program exited successfully with no errors
}

Most of the explanation for this example is in the comments I’ve left here, but I’ll do a quick summary of the code:

  • (Line 1) Include contents of iostream file to enable IO operations.
  • (Line 5) Function declaration of int main() (ran on startup of script).
  • (Line 6–10) Function body of int main(), specified by curly braces.
  • (Line 7) String output to the console using cout from iostream file.
  • (Line 9) Return 0 to signal successful script completion to OS.

This is as basic as C++ gets when it comes to writing scripts, versus our beloved Python.

print("Hello, Medium") # wow much less boilerplate

But we’re not here for less boilerplate, we’re here to become better programmers! Let’s continue on by explaining variables and undefined behaviour.

#include <iostream> // same inclusion operation as last time!

int main()
{
int x; // seemingly fine variable declaration (no assignment yet!)

std::cout << "x = " << x; // same console output!

return 0; // same return statement!
}

This seemingly pleasant program will compile but result in undefined behaviour!! We do the same operations in our previous example, however variable x is simply defined as an integer (data type) with an identifier (given name) of x. We didn’t give it a value!

In programming, when we initialize variables like this, they get stored in memory at a particular address. When we attempt to print out x, we get 0! We never gave x a value to start with! This is called undefined behaviour, and can result in your programming exhibiting a lot of randomness like:

  • Randomly crashing with seemingly no reason
  • Random behaviour like correct/incorrect output with no code changes
  • Program works on x compiler but not y compiler

If you remove the print statement above, you’ll potentially get either an error or warning from your compiler that x is not initialized. In my case, GCC complains of such an error, and fails to compile.

We can fix this program and make GCC happy by defining x with a value.

#include <iostream> // same inclusion operation as last time!

int main()
{
int x { 5 }; // direct list initialization (preffered for C++)

std::cout << "x = " << x;

return 0;
}

Nice! Everything compiles are works out :) Now that we’re done with that, we’re going to move onto our final two topics, which are header files and debugging.

To present header files accordingly, I’ll create a small program with a header file holding some forward declaration to functions in our print.cpp file.

// forwardDeclar.h
void printStuff(); // forward declaration hinting compiler at function somewhere in our program
// print.cpp
#include <iostream>
#include "forwardDeclar.h"

int main()
{
printStuff();

return 0;
}

void printStuff() // this wouldn't compile if we didn't include the forward declaration in our header file!
{
std::cout << "Yay, everything compiled and linked correctly!";
}

I would show you the results of this program, but Medium doesn’t want me to upload the executable running. Anyways, everything compiles and links successfully!

But what if that didn’t happen? What if we broke something?

Consider the following alteration of forwardDeclar.h:

void printStuff();

void printStuff() // this wouldn't compile if we didn't include the forward declaration in our header file!
{
std::cout << "Yay, everything compiled and linked correctly!";
}

// NOTE: never EVER include functions in header files, this is how you get double definitions or break something in a large codebase

Let’s compile now! :D

C:\Users\(username)\Desktop\Programming Projects and Scripts\cpp-projects\print.cpp:11:6: error: redefinition of 'void printStuff()'
11 | void printStuff() // this wouldn't compile if we didn't include the forward declaration in our header file!
| ^~~~~~~~~~
In file included from C:\Users\(username)\Desktop\Programming Projects and Scripts\cpp-projects\print.cpp:2:
C:\Users\(username)\Desktop\Programming Projects and Scripts\cpp-projects\forwardDeclar.h:3:6: note: 'void printStuff()' previously defined here
3 | void printStuff() // this wouldn't compile if we didn't include the forward declaration in our header file!
|

Aw dang it :( The compiler is complaining that printStuff() has been defined twice. This is after we include the contents of forwardDeclar.h. Forward declarations aren’t causing the issue, it’s the double function declaration! For context, this is how our file looks when it caught the error:

// included contents of iostream

// header file contents below:
printStuff();

void printStuff() // this wouldn't compile if we didn't include the forward declaration in our header file!
{
std::cout << "Yay, everything compiled and linked correctly!";
}

int main()
{
printStuff();

return 0;
}

void printStuff() // this wouldn't compile if we didn't include the forward declaration in our header file!
{
std::cout << "Yay, everything compiled and linked correctly!";
}

This doesn’t look right! We need to resolve this issue by removing one of the function declarations to make GCC happy again!

Technically, we can remove the declaration in print.cpp or forwardDeclar.h, however, we should remove the declaration in forwardDeclar since it’s bad form to include function definitions inside header files (they’re better for storing directives pointing to library files or other header files that they branch off to).

Congrats, you just fixed a compiler error through debugging! Debugging is effectively the technique of removing bugs in a program, and what we did is all but a debugging technique. We:

  1. Assessed the issue and read the error.
  2. Understood the problem we created.
  3. Made an adjustment to the program.
  4. Recompiled and tested our program!

./subscribe.exe

That’s all of the knowledge I’ve procured over the past few days! If you guys want me to explain more C++ concepts that I may have missed/want to hear about, lemme know through the comments on this post!

I’ll hopefully see you guys next week with some more info on debugging methods with VS Code’s integrated debugger. Until then, I hope you guys have a great summer!

Have a great rest of your day, Medium.

If you enjoyed this blog post, consider following me or subscribing to support me!

Check out my GitHub and Portfolio!

--

--

nubb

Some guy programming and telling the world about it || Check out my projects here: https://github.com/nubbsterr