23
Aug

How C++ Works


hey what’s up guys my name is the Cherno welcome to another episode of my people are plus theory so today we’re going to learn all about house people as plus works we’re going to kind of try and keep it simple for now but we’re going to learn about how we go from the source file the actual text file to an actual executable binary or program that we can run the basic workflow of writing a simple sub program is you have a series of source files which you write actual text ins and then you pass it through a compiler which compiles it into some kind of binary now that binary can be some sort of library or it can be an actual executable program today we’re going to talk specifically about executable programs or executable binary so let’s hop on over to visual studio and check it out ok so here we have our hello world application that we wrote in the previous video when we learn how to set up C++ on Windows it’s a pretty basic program but there are quite a number of things going on here first of all we have this include iostream statement now this is something called a preprocessor statement anything that begins with a hash is a preprocessor statement the first thing that a compiler does when it receives a source file is it pre processes all of your preprocessor statement that’s why they called preprocessor statements because they happen just before the actual compilation in this case is something called include what include will do is find a file so in this case we’re looking for a file called iostream take all of the contents of that file and just paste it into this current file these files that you include are typically called header files and we’ll discuss them more in depth as the series goes on the reason we are including something called iostream is because we need a declaration for a function called C out which lets us scream stuff to our console next we have this main function now the main function is very important because every simplest program has something like this the main function is called the entry point it’s the entry point for our application that means that when we run our application our computer starts executing code that begins in this function as the program is running our computer will execute the lines of code that we type in order of course there are certain things that can break or change the order of execution and those are primarily called control flow statements or calls to other functions but the gist is that our code gets executed line by line so the first thing that will get executed in our application will be this hello world the hour and then the next thing is this the end gate function and then since that’s all that we’ve got inside our main function our program will terminate now for those of you who are familiar with functions you might notice that the return types of main is actually int however we’re not returning an integer that’s because the main function is actually a special case you don’t have to return any kind of value from the main function if you don’t return anything it will assume that your returning is zero this only applies to the main function there is a special case all right so let’s talk a little bit more about what this is actually doing this kind of syntax might look strange for someone who’s new to C++ and it is a little bit unfortunate that it’s actually written this way because it doesn’t make too much sense when you first look at it but basically these left angular brackets which look kind of like a bit shift left operator are actually just an overloaded operator so you need to think of them as a function now I know they look like an operator but here’s the thing operators are just functions so in this case this would actually be the same thing as if it was something like the out of print and then hello world is our parameter and then maybe we would follow it on along with another print that’s all it is you have to think of these operators as functions and if you think of them that way then it makes a little bit more sense so what we’re actually doing here is we’re pushing this hollow world string into this CR which basically causes it to get printed to the console and then we’re pushing an end line this end line basically just tells our console to advance to the next line the scenes of get function in our case will basically just wait until we press ENTER before advancing to our next line of code which is nothing so basically what I’m saying is our programs execution will pause on this line until we press ENTER because this function is just going to wait for us to press ENTER and then we advance the next line which is nothing which means that we actually return zero meaning our program executed successfully and that’s it that is our entire program okay so that’s our source file we’ve actually got a file called main dot CPP which is a source file how do we get from this text to an actual executable binary file so basically we go through a few stages first we have this include iostream this is something called a preprocessor statement so this preprocessor statement gets evaluated before we compile the file in this case what it does is it include all of the content of the iostream file into this file and I mean literally it just it copies and pastes that file into this file and again as I said we will talk about how to files in more depth in the future so don’t worry too much if you don’t understand this so now all you need to know is we include this file so we can use to see out and see in functions once our preprocessor statements have been evaluated our file gets compiled this is a stage where our compiler transforms all of this C++ code into actual machine code there are several important settings that determine how this actually happens so let’s take a brief look at them in visual studio we have these two important drop-down menus up here once called a solution configuration and once called a solution platform by default it will probably be set to debug and then either x86 or win32 they’re actually the same if we drop down the debug you’ll see that we’ve got two options debug and release these two options are defaults for any new project in Visual Studio and then under solution platform you’ll see we’ve got x64 and x86 as our two options again these are just default a configuration is simply a set of rules which applies to the building of a project whereas the solution platform is what platform we’re targeting with our current compilation so a good examples that will be x86 is targeting windows 32-bit which means that we will generate a 32-bit application for Windows for more complicated projects where you might be targeting different platforms you might have Android as a platform in that drop-down and then if you wanted to build and deploy and debug on Android you would change your platform to be Android and the solution configuration is a set of rules that defines the compilation for that platform so let’s take a look at some of the rules that we can change in our project let’s right-click on it and hit properties so here we have Visual Studio as property pages these define the rules that are used to build them in configurations and platforms the first thing that you need to notice is this configuration and platform area make sure that your configuration and your platform is set to the one that you actually want to modify for some reason sometimes it might be set to release however you’re clearly building debug which means that none of these changes will like to your current configuration and if you missed that you might be wondering why nothing’s working I’ve had that happen a few times it’s kind of annoying so we’ve got our debug configuration you can see we’ve got wind 32 now wind 32 is exactly the same as x86 okay they are the same they’ve got different names for some reasons but they are the same here we’ve got some general information about our SDK version output directory or intermediate directory and stuff like that the important thing to note here is that our configuration type is set to application if we wanted it to be a library we’re good we could change it here but it’s basically the binary there’s a compatible output since we’re going for an executable binary we’ll leave it set to application Exe the compiler settings are located under CC + block we’ve got important settings here such as include directories optimization settings that we may want to use cogeneration settings preprocessor definitions and a whole lot of stuff that we’re not even going to touch anytime soon the default little studio configuration is actually pretty good so we don’t really have to do anything but these are the rules that govern how our files will get compiled you can see the difference between the debug and the released configuration pretty well if you go into the optimization tab under optimization if I change this to release you’ll see that the optimization is set to maximize B whereas in debug it’s set to disabled that’s a great example of why I debug mode by default is slower a lot slower than release mode because optimization is turned off but of course turning off optimization will help us to debug our code as we will later discover if you want to learn more about how the compiler works I’ve made a dedicated video in depth covering exactly how all of that happens so go check that out if you’re interested the link will be in the description below each CPP file in our project gets compiled header files do not get compiled at all just CPP file remember header files get included via a preprocessor statement called include into a CPP file and that’s when they get compiled so we’ve got a bunch of CPP files that we’ve compiled and they actually get compiled individually every CPP file will get compiled into something called an object file the extension for that using visual studios compiler is obj once we have all of those individual obj files which are the result of compiling our CPP files we need some way to stitch them together into one an exe file and that’s where our friend the linker comes in you can see the linker settings under this linker tab but basically what the linker does is it takes all of those obj files and it glues them together so the linkers job is to take all of our obj files and stitch them together into one exe file of course the way that it does that is actually kind of complicated so I’ve made a specific video covering that go ahead and check that out the link will be in the description below so let’s take a look at this in action the first thing I want to do is actually just compile this CPP file in Visual Studio you can compile files individually by hitting ctrl f7 you can see our output here shows that we’re actually building this main del CPP file and that it succeeded if you don’t want to hit ctrl f7 you can actually bring up this compile button you can do so by right-clicking here and clicking on build and then going out or remove buttons customize and then adding a command on the build called compile so if we hit that button you can see that we get our file compiling if we were to make some kind of syntax error here for example I’m forgetting a semicolon if I compile that file you’ll see that we get an error now Visual Studio presents us with errors in many different ways one of which is this error list and another way is inside this output window I’m going to tell you guys right now this error list is mostly garbage it might appear to be readable for really small things like this but you never ever want to rely on it a lot of times it’s actually missing information the weight of the error lists work is it basically passes our output window looking for the word error and then grabs information from there that it can find and puts it into this error lit so it’s it’s a good overview you want to use it like an overview but if you want more details if you want all the information about the error that you’ve just had look at the output window so for the rest of this series I am actually going to be looking at this output window for error messages to get used to that you can see that we’ve got an arrow here it says intact arrow missing semicolon before a curly bracket it tells you which line number the error is on if you double click on this actual line you will be taken to where the error is in your source code so let’s go ahead and fix that by adding a semicolon and then hitting ctrl f7 or compile to build this one file so we’ve compiled a file when you compile files individually no linking happens obviously you’re just compiling a single file so the linker is an invoked apple let’s go ahead and check out what the compiler actually generated if we right click on our hollow world project you’ll see an open folder in File Explorer button this will open up our File Explorer by default visuals view will output our build files into this debug folder and you can see if we go in there you can see a mean obj file this is the object file that our compiler has generated again you will have one of these for every single simple file in your project if we are back to visual studio and we build the actual project so I’m doing more than just building one file here I’m actually building the entire project you can see that we actually get that exe file and again if we go back to our file explorer that will actually be in the directory of your solution and then in the debug folder I know visual Studios default paths are a little bit weird I usually like to change them but I’m trying to not to complicate things here and there’s our hello world exe file which we can run and it prints the text hello world so that’s a pretty simple overview but what happens when we have multiple people plus files let’s take a look at a simple example so suppose that we’ve got our hollow world printing the pecans all here but I don’t want to use the see out function I want to use my own logging function and then maybe that will wrap this see our function so let’s create a function called log which will take in a C string called message and print that message choose a contour simple enough now don’t worry if you’re not sure what a Const char pointer is we’re going to talk about strings in another video for now all you have to know that a Const our pointer is basically just a type that can hold a string of text so now we can rewrite our code so that instead of calling C out and then printing hello world we call this log function and then pass in hollow world as a parameter we can go ahead and hit the local windows debugger button here just to make sure that it still works as you can see it does fantastic we’ve written our first function that was easy so now let’s take that function and what it into a different file because I don’t want to have this main does typically files flooded with all of my code I want to separate my code into multiple files to keep things nice and clean and organized we’ll make a new file under source files like going a right-click add new item we’ll make a CPP file we’ll call it log build CPP and we’ll click Add so what I’m going to do here is I’m going to go back to main and I’m going to cut this log function and paste it into here so now we’ve got a function inside our logos with a file called log let’s try and compile just this file okay check this out we get a bunch of errors and if we look at our output window you’ll see that the out is not a member of STD basically is telling us that it has no idea what CL is the reason is because we haven’t included a declaration for the out every kind of symbol in saqqaq lot needs some kind of declaration the out is defined inside a file that we included in main dot cpp and that file of course was iostream let’s go ahead and grab iostream and put it at the top of this file so that we include iostream by doing so we include a declaration for this to be our function let’s go ahead and compile this file once again you can see now it succeeds great so back in main I want to call this log function can I do that let’s hit ctrl f7 no I can’t because log is not found we also get a complaint about the end but we already know that that’s because we removed the i/os trim included and we have no idea what bian is we can restore that include and our problem should be fixed however you can see that log is still not found so what’s going on here we’ve moved a function from one file into the other and we are compiling each file separately for the minute so this main dot CPP file has no idea that there’s a function called log somewhere and since it doesn’t recognize what log is it gives us the compile error we can fix this by providing something called a declaration a declaration is exactly what it sounds like we’re declaring that something called log exists now this is almost like a promise because we can just say hey compiler there’s a function called log however the compiler will just believe us and that’s the great thing about the compiler it’ll be like oh yeah cool I totally trust you because the compiler doesn’t care about resolving where that log function actually is defined we have two different words here declarations and definitions declarations are just a statement which say hey this symbol this function exists and then a definition is something that says this is what this function is this is the body need for this function so let’s go ahead and write a declaration for our log function a declaration looks very similar to an actual definition this is something called a definition because you can see that not only have we declared something with the name logger function with the name log we’ve also given it a body which actually contains what code will run when we call this function so back in main let’s write a declaration and a declaration looks very similar to a definition however what it doesn’t have is the actual body so you can see I can just put a semicolon at the end of this and that is the end of that in fact you don’t even have to specify the name of the parameter because it doesn’t matter you can just write that and the rule of thumb though I do like to specify the name because it makes more sense so let’s compile this file now tallip check this out the compiler totally bought it so you might be wondering at this stage what will hey how did the compiler know that we actually have a log function in another file if we’re just compiling this one file and the answer is it doesn’t it just it trusts us so then your second question should be how does it actually run the right code that is where the linker comes in when we build our entire project not just this one file but if I actually right click and hit build once our files have been compiled the linker will actually find the definition of that log function and wire it up to the log function that we call here in main dot CPP if it can’t find that definition that’s when we get a linker error now linking errors or something look very scary and a lot of people get scared so let’s go ahead and look at an example of that so right now if I just run my program you’ll see that it still prints out the text hello world and everything runs successfully however let’s remove this or at least change it a little bit for example I’ll change this to logger save the file go back here to main Datsyuk B I’ll try and compile this file by itself you can see that we get no problems whatsoever however let’s right-click on this hello world and hit field and check this out we get a pretty scary looking error message which also and unfortunately looks scary in our output window now the reason this looks those two carries because it’s actually got some extra information about our functions ignition for example it’s got the pulling convention here as well as an actual ID but basically what it’s telling you is that you have an unresolved external symbol called log which returns which has this return value and these parameters and your referencing this function inside main an unresolved external symbol means that the linker was unable to resolve a symbol remembered linkers job is to resolve symbols it has to wire up functions and it couldn’t find what to wire log to because we don’t have a function called log that’s actually defined that has a body so the way that we can fix this is by fixing our function we need to provide a definition for this log function in other words we have to provide a body for this log function doesn’t have to be inside this file it can be inside main but it has to be somewhere and if we compile this you’ll see that we don’t get any errors if we go back to our file explorer and look at what we’ve got inside our intermediate folder here you’ll see that we’ve got two obj files because the compiler generates an object file for each of our CBP files the linker will then take them and pitch them together into an exe file so in our lovely example we have our log definition inside this log obj file and our main function inside our main door obj 12 and so link up will basically take that log definition from log and put it into a common binary which is our hello world XP file which contains a definition for both main and log and that is a basic overview of how C++ works again I highly encourage you to check out the in-depth videos about how compiling and linking works because they’re going to be way more information than this video this video was just made to show you kind of an overview of the pipeline of how you go from source files to an actual binary as always don’t forget to follow me on Twitter and Instagram if you really enjoyed this video and you want to see more you can support me on patreon I’ll see you guys next time goodbye war

Tags: , , , , , , , , , , , , , , , , , , , , , , , , , ,

100 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *