wkk0
wkk0
Payton Yao
February 27, 2017
1 Introduction
Throughout this first week, we’ll be delving into what happens when you run programs. We’ll have a
look at what happens when you compile your program, what it looks like when you run them, and reasons
why that’s important. We’ll have some experiments and exercises to understand what is really happening.
2 Command Line
Many of you already know how to code. Some way or another, you’ve managed to solve some problems
from the NOI allowing you to get this far. For some of you, you might have programmed directly into
HackerRank’s web interface, compiling and debugging your programs through that platform. For others,
you might have used an Integrated Development Environment (IDE) like CodeBlocks or DevC++. For those
unaware, IDEs combine multiple tools in one, allowing you to edit source code, compile programs, debug
them, and more, all in one convenient program. From our understanding, very few of you do everything
through the command line which is what we’ll introduce here.
You might be asking: ”Why?” If everything can be done on an IDE, why bother learning to use the
command line? There are two answers, both equally valid. The first and more practical one is that you may
not have the exact IDE you’re comfortable with available at all times. Especially at the IOI, HackerRank
definitely won’t be available. Every computer will have some form of command line available, and every
computer meant for programmers will have the command line tools set up. The second reason is about
understanding. IDEs give way to too much magic and sometimes we miss all the small things that happen
behind the scenes.
Note that this is only a short introduction into the command line. Mastering the use of the command
line takes years of experience to do extremely intricate things, but basic usage allowing you to navigate the
file system, compile code, and run programs you can learn in the space of an hour or so.
At this point, it’s important to note that there are some major differences between using the command
line on Windows and on Linux. For Mac users, you can follow along on the Linux path since the differences
are small. Please read the appropriate section based on your operating system.
As a historical note, there was once an operating system called Unix developed at Bell Labs in the 1970s.
It’s this original operating system whose design philosophy Linux and Mac copied. Because they come from
similar roots, much of the core command line tools are the same with some small differences in some of the
options they can be given. Advanced users will also note a striking similarity in the way system files are
organized, for example Linux has /home/ where Mac has /Users/.
2.1 Windows
On Windows, you have two main choices of terminals: Command Prompt (cmd.exe) and PowerShell
(PowerShell.exe). PowerShell is the newer (and more powerful) one. Command Prompt has a width limit
of 80 characters, so it’s kind of annoying. Run them from the Start Menu or the Windows 8 search.
The important commands are as follows:
• cd - change cirectory, use as cd .
1
• dir - list files in directory, use as dir C:\Users\Public
• copy - copy a file, use as copy a.txt b.txt
• move - move a file from one place to another, use as move original.txt new.txt. Interestingly, this is
also the command used to rename files.
3.1 Installing
3.1.1 Windows
Please go to https://wiki.wxwidgets.org/Installing MinGW under Windows. There are more detailed
instructions on the link. Make sure you don’t forget to add C:\MinGW\bin to your Path environment
variable.
3.1.2 Mac
For Mac users, you have to install Xcode then download the command line tools from there.
3.1.3 Linux
For Linux users, g++ comes installed on the base operating system.
2
3.2 Using g++
Now that you’re all done setting up, it’s time to start compiling! If you save your source code file as
source.cpp, you can compile it using the command g++ source.cpp. By default it saves the resulting
executable as a.out.
On Windows, the default name of the executable is a.exe. You can change the name for the resulting
output file by adding -o filename to the command. For example, if my source code is example.cpp and I
wanted to save the output executable as executable I should then type the command g++ example.cpp -o
executable. Note that for Windows users, the resulting executable name should end with .exe otherwise
Windows won’t run it.
We recommend that you try compiling a sample Hello World program this way before moving on.
If you’re having an error like g++: error: source.cpp: No such file or directory, please en-
sure you’re in the right directory first.
4 Input redirection
4.1 Saving Output to a File
There are cases where we want to save the output of our program to a file. The simple way is to highlight
and copy things from the command line, paste it in notepad, and save the file. But there’s an easier and
more precise way to do it. Use the > symbol.
To be precise, if we want to save the output of the program executable to a file called output.txt, we
type the command ./executable > output.txt.
On windows, the corresponding command is executable.exe > output.txt.
Try it yourself! Of course, you can change the output file name to anything you want and not just
output.txt
It’s important to note at this point that this will completely erase the contents of the file you’re
saving into, even if your program hasn’t printed anything out yet. So be careful!
3
4.3 Combining the Two
There are cases where we have input from a file and we want to save the output to another file. During
the Google Code Jam, input files are downloaded from their website and you have to upload the correct
output file. Or maybe you just want to store the output to a pre-determined input. The way you do that is
just combining the two.
An example command that combines them is ./executable < input.txt > output.txt
Note that the command ./executable > output.txt < input.txt is just as valid and does exactly
the same thing.
I’m sure the Windows users among you can get the pattern by now, so I won’t belabor the point.
6 Pointers
Pointers allow you to access the slot numbers themselves. The official term used is the address. Every
variable has an address which is a 64-bit number. Some older computers from the early 2000s might still
4
have addresses as 32-bit numbers because RAM rarely exceeded four gigabytes back then, but for the most
part addresses nowadays are 64 bits long.
The address of a variable can be gotten by using the & operator. One naive way to store it is as a long
long, but as we mentioned earlier, does this number refer to a char or a long long or maybe something
even bigger?
In C++, the pointer type is what is used to tell the computer how we want to interpret these 64-bit
addresses. To declare a pointer variable, we declare a variable with a *. For example, we can do:
void main() {
int x;
int* pointer_to_x = &x;
}
In case any of you become confused with examples on the internet, most other sources will put the *
declaration beside the variable name as in int *pointer to x. Both of these mean the same thing, but I
find more meaning in the way I prefer to do it. Unfortunately, the designers of the original C prefer the
second because int *ptr, x makes a pointer to an int and then a normal int. If you wanted two pointers,
you’d have to declare int *ptr, *x. I will not be doing this in any of our examples because I believe that
the variable type is a ”pointer to an int” and not an ”int that happens to be a pointer”. This is a stylistic
preference, so do what you want. The C++ compiler doesn’t care how you put the spaces.
Of course, you can do this even more.
void main() {
int x;
int* pointer_to_x = &x;
int** pointer_to_pointer_to_x = &pointer_to_x;
int*** pointer_to_pointer_to_pointer_to_x = &pointer_to_pointer_to_x;
}
At some point it gets a bit silly. But we will find a use for this kind of thing later when we talk about
arrays.
To access the memory at that address, we can use the * operator, known as the dereference operator.
Don’t get confused by the multiplication operator with the same symbol, the dereference operator is unary,
similar to how the unary negative operator is different from the minus operator.
The type of the pointer tells the computer how many bytes to interpret starting from that address. For
example, look at the following:
#include <iostream>
using namespace std;
int main() {
unsigned int x = (3 << 24) + (5 << 16) + (7 << 8) + 11;
5
Now you may be wondering why we would even want to mess with the memory address of variables
instead of the variables directly. The above example is clearly really contrived. So let’s give a simple one
for the sake of example. Suppose we wanted a function to return two variables. But C++ only allows one
variable at a time. The more clever ones among you might consider packing multiple values in a single 64-bit
number. The ones who know a bit more C++ might think of using structs. But there’s something done in
the standard C library that uses pointers, and I think it’s pretty great. The function in question is called
scanf. You use it like: scanf("%d %d", &a, &b"). Note that it asks for the address of the variables it
should store the data in. The implementation of such a function would look like this:
#include <iostream>
using namespace std;
int main() {
int a, b;
foo(&a, &b);
cout << a << " " << b;
}
6
7.3 Zero-based Indexing
Earlier we mentioned that to get the nth item in an array, we look at ptr + (n-1). But if we just started
counting from 0, then we can save a subtraction! The 0th item is at address ptr, the 1st item is at address
ptr + 1, the 2nd item is at address ptr + 2. And the kth item is at address ptr + k. This trick is the reason
why programmers like to count from 0.
As a side note, there are some programming languages that are not zero-indexed and their compilers
automatically add -1 when arrays are accessed. That’s not C++ though, so no need to worry about it.
int total_allocated = 0;
void go_deeper() {
char arr[10000];
total_allocated += 10000;
printf("I have allocated about %d bytes of memory.\n", total_allocated);
go_deeper();
}
int main() {
go_deeper();
}
On my computer, this code goes deep enough to allocate about 8 megabytes before it crashes. But my
computer has several gigabytes of memory. So what happened? And how do we access the rest of our several
gigabytes of memory (or hundreds of megabytes in contests)?
7
#include <stdio.h>
#include <malloc.h>
int total_allocated = 0;
void go_deeper() {
char* arr = (char*)malloc(10000);
// This malloc call gives you 10000 bytes to play with.
// It doesn’t have any idea of the meaning of this chunk of memory,
// so it’s your job to convert it to a pointer of the correct type.
total_allocated += 10000;
printf("I have allocated %d bytes of memory.\n", total_allocated);
go_deeper();
}
int main() {
go_deeper();
}
If you open Task Manager while this program is running, you’ll notice that it’s slowly eating up more
and more of your memory. You might want to kill it before it goes too far and your computer starts lagging.
Or you could also wait for your computer to crash or the program to crash because of memory issues. :)
int main() {
int* arr = (int*)malloc(sizeof(int) * 10000);
}
sizeof is also really useful for when you define our own custom data types. One day you might want
to make our data type take 16 bytes, but then you update it so it needs 24 bytes. In that case, you don’t
8
want to go through all our code to replace all the references of malloc(16 * x); with malloc(24 * x);.
Instead we can just do malloc(sizeof(custom data type) * x);.
9
#include <stdio.h>
int main() {
if (’A’ == 65) {
printf("’A’ is equal to 65\n");
}
if (’z’ == 122) {
printf("’z’ is equal to 122\n");
}
printf("Let’s print the %c character\n", 65);
}
You might notice from this that the ’x’ notation for characters is just convenience for letting the compiler
look up the number for you during the compilation stage. There’s not very much reason to look up the
character yourself when the compiler can do it. The important thing is that the equivalence of letters and
numbers allows you to do operations on characters. The ASCII table was designed for a purpose, so it’s not
just completely jumbled garbage. Digits are consecutive on that table, and so are capital letters and small
letters. For example, if you wanted to capitalize letters, you just need to do some arithmetic to convert the
range 97-122 (’a’ to ’z’) to 65-90 (’A’ to ’Z’).
This is practically all you’ll need to know about character encoding for programming contests.
int main() {
char string[1000];
cin.getline(string, 1000);
for (int i=0; i<1000; i++) {
// Terminating character
if (string[i] == 0) {
break;
}
10 C++ Structs
We have been mentioning defining our own data types for a while now. It’s time we learn to do that now.
10
Let’s start with a simple problem. We want to write a function that computes the simplest form of a
fraction.
If you actually try to think of how to do this, you’ll quickly run into a problem: Fractions are represented
by a numerator and a denominator. But functions can only return one value. If you’re willing to mess
around, I’m sure some of you will be able to think of solutions to this question.
Using what you already know by now, I can imagine three tricks to get around this problem. The first
way is to store return values in global variables and copy them over as soon as the function is done. A
second way is to accept pointers and store the answers in the given addresses. And third, allocate some new
memory, store the answer there, then return a pointer to that memory address. Any of these solutions will
work, but they don’t fit into the contrived story for this section! Not to mention it’s going to be difficult to
work with these things.
Wouldn’t it be nice if we could just bundle variables together in a simple and elegant way? Then we
could return those bundles so we’ll be able to return two variables. And taking that idea further, we can pass
those bundles as arguments to functions, saving us lots of typing and error-prone code. What a wonderful
world that would be if only we could bundle variables together.
Well I have good news for you! C++ supports exactly that through a feature called a struct! Another
name you might read on the internet for a struct is a record, which is the term used for the same thing in a
few older programming languages (and even some newer ones like the Starcraft II modding language called
Galaxy). To understand what a struct is, let’s look at some sample code.
struct fraction {
int numerator;
int denominator;
};
The sample code above defines a struct called fraction. In this case, fraction is a bundle of two int
variables. The variables that it groups together are called its members. In this case, our fraction struct has
two members called numerator and denominator. Let’s look at some sample code to see how we can use our
new data type.
#include <stdio.h>
#include <algorithm>
struct fraction {
int numerator;
int denominator;
};
fraction simplify_fraction(fraction f) {
// We simplify a fraction by dividing its numerator and denominator by their gcd.
int g = gcd(f.numerator, f.denominator);
fraction answer;
answer.numerator = f.numerator / g;
answer.denominator = f.denominator / g;
return answer;
11
}
int main() {
// create a new fraction called ‘unsimp‘
fraction unsimp;
unsimp.numerator = 15;
unsimp.denominator = 20;
11 Homework
11.1 Linked List
Implement a linked list of int from scratch. In particular, fill out the commented sections of the code
below to make it work. You are free to add any structs you want.
#include <iostream>
using namespace std;
struct linked_list {
// Fill out
int length;
};
linked_list* linked_list_create() {
// Fill out
}
12
void linked_list_destroy(linked_list*) {
// Fill out
// Make sure you don’t have memory leaks!
}
int main() {
// Input is lines of the form:
// add v
// remove idx
// get idx
while(1) {
string instr;
cin >> instr;
if (instr == "add") {
int v;
cin >> v;
linked_list_add(list, value);
} else if (instr == "remove) {
int idx;
cin >> idx;
linked_list_remove(list, idx);
} else if (instr == "get") {
int idx;
cin >> idx;
cout << linked_list_get(list, idx) << endl;
} else if (instr == "length") {
cout << linked_list->length;
} else if (instr == "exit") {
break;
}
}
linked_list_destroy(list);
}
13
Subtask 2. Allocate a jagged array (i.e. a 2D array with each subarray not having equal size) from the
heap. Row i should have space for (i+1) elements.
11.3 Problems
UVa 11278 - One-Handed Typist
UVa 554 - Caesar Cypher
14