[In this reprinted #altdevblogaday in-depth piece, Gamer Camp's technical course lead Alex Darby continues his series on a C/C++ Low Level Curriculum by looking at conditional statements.] Hello, interwebs! As the title suggests, this is the sixth part of the C / C++ Low Level Curriculum series I've been doing. In this installment, we'll be starting to look at conditional statements, and what the code that you're asking the compiler to generate when you use them looks like (at least before the optimizer gets to it…). Just in case anyone is unclear about what they are, conditionals are the language features that allow us control over which parts of our code get executed. At face value, the subject of conditionals might seem a simple one, but it is precisely because it seems simple – and because so much else builds on top of it – that it is the first topic that I've chosen to look at in detail after function calls. Though we won't get around to all of them in this post, our look at conditionals will take us on a tour through a representative sample of x86 disassembly generated by if statements, the conditional operator (or "ternary operator", or "question mark"), and switch statements; and whilst we look at all of these we'll also be looking at disassembly generated by the (built in!) relational and logical operators that are used with them (i.e. ==, !=, <=, >=, >, <, !, &&, and ||). Prologue Firstly, I'd like to apologize to anyone who reads these posts regularly for the fact that my rate of posting has slowed down – I will hopefully speed up again to the regular 2 week posting cycle in the near future. Secondly, here are the backlinks for anyone who wants to start from the beginning of the series (warning: it might take you a while, the first few are quite long):
A Low Level Curriculum for C and C++
C / C++ Low Level Curriculum part 2: Data Types
C / C++ Low Level Curriculum Part 3: The Stack
C / C++ Low Level Curriculum: More Stack
C / C++ Low Level Curriculum Part 5: Even More Stack
Generally I will try to avoid too much assumed knowledge; but if something comes up that I've explained previously, or that I know another ADBAD author has covered already then I will just link to it; this implies that you, dear reader, should assume that I assume you will read anything I link to if you want to make complete sense of the article :) Compiling and running code from this article I assume that you are using Windows, are familiar with the VS2010 IDE, and comfortable writing, running, and debugging C++ programs. As with the previous posts in this series, I'm using a win32 console application made by the "new project" wizard in VS2010 with the default options (VS2010 express edition is fine). The only change I make from the default project setup is to turn off "Basic Runtime Checks" to make the generated assembler more legible (and significantly faster…) see this previous post for details on how to do this. To run code from this article in a VS2010 project created this way, open the .cpp file that isn't stdafx.cpp and replace everything in it with text copied and pasted from the code box. The disassembly we look at is from the debug build configuration, which generates "vanilla" unoptimized win32 x86 code. Instructions and Mnemonics: an aside I've just realised that so far in this series I have typically been using the term instruction when referring to an assembler mnemonic. I felt that I should point out that this isn't 100% accurate, because whilst assembler mnemonics are normally thought of as having a 1:1 correspondence to binary CPU instructions, they are not actually instructions. In fact, in x86 assembler, the mnemonics often actually have a 1:x relationship with the corresponding opcodes, because multiple variants of each mnemonic exist that differ in the types and sizes of their operands. This is not something you should worry yourself about too much, as it's a fairly harmless Kenobiism, but I still felt I should point it out if I was going to carry on doing it ;) Conditionals The best place to start is, as someone or other famously once remarked, at the beginning; so let's start with the most basic form of the if statement. Before anyone mentions it, I know I could have omitted the curly braces around iLocal = 1; on line 9. If you're the kind of person who's so lazy that you like to leave out curly braces in these situations then that's up to you; but I would just like to point out that there is probably a special place in one of the deeper and less pleasant circles of the Hell I don't believe in that is reserved for your sort – just a couple of floors up from those who do the same thing with loops. Also, I've left the #inlcude "stdafx.h" in the code box so that your line numbers match mine if you're working through this yourself.
#include "stdafx.h"
int main(int argc, char* argv[])
{
int iLocal = 0;
if( argc < 0 )
{
iLocal = 1;
}
return 0;
}
Anyway, as usual if you're looking at this in VS2010 then copy and paste the above code over whichever is your project's main .cpp file, put a breakpoint on line 7, tell Visual Studio to compile and run, wait for the breakpoint to be hit, then right click in the source window and choose "Go To Disassembly". You should now be seeing something like this:
n.b. right-click and check you have the same options checked as me...
As we already know the assembler above int iLocal = 0; is the function prologue (or preamble) and the assembler after the closing brace of main() is function epilogue. The specific disassembler we're interested in is between lines 7 and 13 of the source code that is shown inline with the disassembly, so here it is pasted into a code window (N.B. the addresses corresponding to the disassembly instructions will almost certainly differ on your screen if you're running this yourself…)
7: if( argc < 0 ) 010D20B0 cmp dword ptr [argc],0 010D20B4 jge main+1Dh (10D20BDh) 8: { 9: iLocal = 1; 010D20B6 mov dword ptr [iLocal],1 10: } 11: 12: return 0; 010D20BD xor eax,eax 13: }
Straight away, there are a couple of new assembler mnemonics we've not come across so far in this series of posts. We'll cover these as we come to them. line 2 is comparing argc against 0. The instruction cmp doesn't have an instant effect on code execution, it compares its first and second operand and stores the result of the comparison in an internal register of the CPU known as EFLAGS. line 3 uses the mnemonic jge, which means jump greater equal. It will cause a jump to the address 0x010D20BD supplied as its operand if the outcome of the previous cmp instruction has set the content of the EFLAGS register to indicate that it
No tags.