CS 10
CS 10
THROUGH C++
with
Manoj Prabhakaran
Lecture 10
Anatomy of a Program
Pre-Processor, Scopes, Namespaces
Library
Header files Object files
Compilation
¥ Header files typically have the declarations of the functions (and more) in the library
¥ Object files are the binary compiled version of functions
¥ It saves time to have the library functions pre-compiled
Compiling a Program
Compile into Object files
Creating the
Write Library Pre-processor Compiler Library
Header files Object files
Compilation
¥ Header files typically have the declarations of the functions (and more) in the library
¥ Object files are the binary compiled version of functions
¥ It saves time to have the library functions pre-compiled
Pre-Processing Steps
¥ Some text transformations
¥ Line ending with a \ is merged with the following line (example later)
¥ Comments are stripped
¥ Line comments: // comment till end of line
¥ Block comments: /* comment spread
over multiple lines */
¥ #include and other pre-processor directives (coming up) are processed,
line-by-line
¥ Processing one directive can result in the appearance of another
directive. They are processed until no more directives are present.
¥ But same directive is not applied twice (to avoid infinite invocations)
numbers.h #include Demo
int GCD(int, int); // contents of file iostream
int LCM(int, int); // tens of thousands of
// lines ...
bool coprimes(int,int);
bool covers(int w, int x);
int GCD(int, int);
bool PFE(int w, int x);
int LCM(int, int);
int reduce(int w, int x);
bool coprimes(int,int);
main.cpp bool covers(int w, int x);
#include <iostream> bool PFE(int w, int x);
$ g++ -E -P main.cpp
#include "numbers.h" int reduce(int w, int x);
Pre-processor
int main() { int main() {
... ...
} }
iostream Headers Containing Headers
// contents of file iostream
// tens of thousands of
... ¥ Need to be // lines ...
#include <ios> careful to avoid
// has content from files
#include <istream> an infinite cycle // included by iostream
#include <ostream> of inclusions! // and files included in
#include <streambuf> // those files, and so on.
... int main() {
...
main.cpp }
¥ Need to be
careful to avoid
inc.h an infinite cycle
#include "inc.h" of inclusions!
main.cpp
Pre-processor error: #include nested too deeply
#include "inc.h"
int main() {
...
¥ There are pre-processor directives that can
} be used for conditional inclusion: coming up
#define
¥ #define VARIABLE value
makes the pre-processor replace the text VARIABLE with the text value
(when appearing as a ÒtokenÓ Ñ e.g., not inside a string literal)
#define DELTA 1e-6
#define main_program int main()
#define DEBUG_ENABLED
#ifndef _INC_DONE
#ifdef _INC_ALMOST_DONE
#define _INC_DONE
#else
#define _INC_ALMOST_DONE
#endif Exercise: Explain how this happens
hello
#include "inc.txt"
bye Testing preprocessor.
#endif Not a valid program.
hello
main.cpp
hello
Testing preprocessor. Pre-processor
bye
Not a valid program.
bye
#include "inc.txt"
Source File after Pre-Processing
¥ After pre-processing a source file has any number of:
¥ Declarations (global variables and functions)
¥ Struct definitions
¥ Function definitions (and templates)
¥ (More later)
¥ A function definition has:
¥ Return type, function name, and parameter list
¥ Followed by statements enclosed in { É }
¥ Different kinds of statements (declaration with or without initialisation,
expression; , conditional statement, conditional loop statement, break,
continue, and return statements, É)
¥ Compiler produces a single object file for each such source file
Scope of Variables
{
¥ In C++, a variable can be used only {
// not visible here (before declaration)
where its declaration is ÒvisibleÓ int x;
// visible here
¥ Visible only within the ÒblockÓ
scope of x
{
// visible here
it is declared in }
// visible here
¥ And only after it is declared }
// not visible here (outside the block)
¥ Scope of a variable: region in }
scope of x
{
... ...
// visible here
} } }
int g; // a global variable. remains visible till the end of the file
...
void f(int x) { // x is visible inside the body of the function
int y; // visible from here till the end of the function
for(int g=x; g<3; gÑ) { // a new local g! visible till
...
... // the end of the for statement.
} // now this g goes out of scope. global g visible again.
{ // start of a new scope
g = x + 1; // this refers to the global g
float g; // this is a different g! global g not visible.
} // now this g goes out of scope. global g visible again.
g++; // global g
} // here x, y go out of scope.
Namespaces
¥ Standard library contains useful functions (swap, max, min, distance,
begin, end, sort, move, É), data types (string, vector, list, É) and global
variables (cout, cin, É), many with common names
¥ But this can be problematic, especially due to function overloading!
¥ Suppose you write a function to_string as follows:
#include <simplecpp>
string to_string(short x) { return x==0 ? "zero" : "non-zero"; }
int main() {
short a = 1; int b = 1;
cout << to_string(a) << " vs. " << to_string(b) << endl;
}
invokes our invokes to_string non-zero vs. 1
to_string from the standard
library!
Namespaces
¥ To keep entities (functions, types, variables) in a library separate from ours
¥ to_string vs. std::to_string
¥ <simplecpp> has a statement using namespace std; which made
all the entities in std namespace available without the qualifier std::
¥ We shall instead use the standard header <iostream>
Risky!
#include <iostream>
std::string to_string(short x) { return x==0 ? "zero" : "non-zero"; }
int main() {
short a = 1; int b = 1;
std::cout << to_string(a) << " vs. " << to_string(b) << std::endl;
}
Invokes our to_string, with b cast into a short.
Only std::to_string invokes the one from the library.
numbers.h Example numbers.cpp
Demo
namespace num { #include "numbers.h"
int GCD(int, int); #include <cmath>
#include <iostream>
#include "numbers.h"
using std::cout; using std::cin; using std::endl;
int main() { $ g++ -c prog.cpp # this produces prog.o
cout << "Enter 2 positive numbers: ";
$ g++ -c numbers.cpp # this produces numbers.o
int a, b; cin >> a >> b;
$ g++ prog.o numbers.o # this produces a.out
if (a<=0 || b<=0) return -1;
cout << (num::PFE(a,b) ? "":"Not ") << "PFE" << endl;
cout << "GCD(a,b) = " << num::GCD(a,b) << endl;
}