Lecture 4 - Sept 14, 2023
Summary
In this lecture, we review what’s a reference variable, and delve deeply into what happens when we compile a multiple file program.
Last lecture
Functions: pass-by-value and pass-by-reference.
Today
Multiple file programs and separate compilation.
References
A reference is an alias, an alternate name, to a variable.
- Reference cannot be re-assigned.
- Must be initialized when declared.
- Reference does not have a separate memory location.
Program organization
We want to split code accross multiple files.
- Organized
- Easier to collaborate
- Faster to compile - Why? Today’s topic.
Single file program
#include <iostream>
using namespace std;
void printNum(int x);
int userInputNum();
int main() {
int num;
= userInputNum();
num (num);
printNumreturn 0;
}
void printNum(int x) {
<< "The number is " << x << endl;
cout }
int userInputNum() {
int x;
<< "Enter integer: ";
cout >> x;
cin return x;
}
To compile, run the following command in the terminal:
g++ lecture4-example1.cpp -o lecture4-example1.so
This command generates the executable file, which contains zeroes and ones.
Multiple file program
main.cpp
Correction to the example from lecture 3: We do not need #include <iostream>
nor using namespace std
because we are not using cout
and cin
in this file.
// "": searches in current directory
#include "print.h"
#include "input.h"
int main() {
int num;
= userInputNum();
num (num);
printNumreturn 0;
}
print.h
void printNum(int x);
input.h
int userInputNum();
print.cpp
We include iostream
here.
#include "print.h"
#include <iostream>
using namespace std;
void printNum(int x) {
<< "The number is " << x << endl;
cout }
input.cpp
#include "input.h"
#include <iostream>
using namespace std;
int userInputNum() {
int x;
<< "Enter integer: ";
cout >> x;
cin return x;
}
Organization
- Function declaration goes in header files (i.e., .h files)
- Function implementation goes in source files (i.e., .cpp files)
Compilation
To compile this program with multiple files, there are different ways.
Case 1
To compile, run the following command in the terminal:
g++ main.cpp print.cpp input.cpp -o main.so
This command needs some explanation.
What happens under the hood? Remember the diagram from lecture 3.
- Source file: Has function implementations (e.g,
print.cpp
,main.cpp
). - Header file: Has function declarations (e.g.,
print.h
,input.h
). - Compiler: Converts high-level programming language to machine language.
- Object file (cannot be executed): Has machine code with references to other variables in another file (e.g.,
main.o
) - Linking: A linker combines object files to produce one executable.
- Executable file: Can be run directly on a CPU.
Story of compilation:
#include
belongs to a class of instructions called pre-processor directives.- Before compiler compiles to object files, pre-processing textually replaces
#include "file-name"
with contents of the file name. - Compiler converts pre-processed C++ code to an object file.
- Object file has machine code with references to other files.
- Linking appends function implementation in object files to create an executable.
- Linking takes much less time than compilation.
Case 2
Since linking takes less time compared to compiling, we can do separate compilation into
g++ -c main.cpp
producesmain.o
g++ -c print.cpp
producesprint.o
g++ -c input.cpp
producesinput.o
g++ main.o print.o input.o -o main.so
producesmain.so
What if I update…
main.cpp
print.o and input.o will not change. Only main.o will change.
g++ -c main.cpp
g++ main.o print.o input.o -o main.so
print.cpp
main.o and input.o will not change. Only print.o will change.
g++ -c print.cpp
g++ main.o print.o input.o -o main.so
print.h
This changes #include "print.h"
in main.cpp and print.cpp. This is tricky!
g++ -c main.cpp
g++ -c print.cpp
g++ main.o print.o input.o -o main.so
IDEs, like VS Code, and Make files keep track of what file you changed and minimizes compile commands as they take time.
In short, dividing code across multiple files can save compilation time.