Home | >> Linear Collections and Modularity
2017-09-07
- Read 2.2, 4.3
- running a program from the command line
./program-name or path/to/programe-name
Note: . means current directory
Providing input: 2 ways
-
./program-name arg1 arg2 ... argn- args are written into the program's memory
- [code][n + 1 (argc)][arg1][arg2][...][argn] (argv)
-
./program-name- (Then type something)
- input comes through standard input stream (stdin)
- keyboard -> program -> stderr (never buffered) OR -> stdout (maybe buffered) -> screen
Redirection
./my-program < infile > outfile 2>errfile
note: 0 and 1 exists respectively, but only 2 is needed since it shares the same operator
Consider (C):
#include <stdio.h>
void echo(FILE *f) {
for (int c = fgetc(f); c != EOF; c = fgetc(f))
putchar(c);
}
}c is an int and not char because other information cannot be expressed as a char, for example EOF (end of file)
int main(int argc, char *argv[]) {
if (argc == 1) {
echo(stdin);
} else {
for (int i = 1; i < argc; ++i) {
FILE *f = fopen(argv[i], "r");
echo(f);
fclose(f);
}
}
return 0; // Status code given to shell (echo $?)
}note:
argv[0] = program-name
...
argv[argc] = NULLObserve: cmd line args / input from stdin - 2 different programming techniques
To compile: gcc -std=c99 -Wall my program.c -o myprogram
gcc translates C source to executable binary
-Wall means warn all, gives warning that only appear if asked for
-o means what to rename program
This program is a simplification of the linux cat command
cat file1 file2 ... filen opens them and prints them one after another
cat - echos stdin
cat < file
- File used as a source for stdin
- The shell (not cat) opens the file
- Displays
Can we write the cat program in C++?
- Already valid C++
- The "C++" way
- Command-line args are the same as in C
- stdin/stdout: #include
#include <iostream>
int main() {
int x, y;
std::cin >> x >> y;
std::cout << x + y << std::endl;
}std::cin, std::cout, std::cerr: streams, types
std::istream: (cin)
std::ostream: (cout, cerr)
>> input operator: (cin >> x), populates x as a side-effect, returns cin
<< output operator: (cout << x + y), prints x + y as a side-effect, returns cout
These operators return cin/cout so they can be chained:
From above: std::cin >> x >> y;
std::ifstream f{"name-of-file"}; // ofstream for output
char c;
while (f >> c) {
std::cout << c;
}*f >> c
- implicity converts to a bool
- true = read succeded
- false = read failed
Input: the quick brown fox Output: thequickbrownfox
Stream input slips whitespace (just like scanf)
To include whitespace:
std::ifstream f {"name-of-file"};
f >> std::noskipws;
char c;
...Note:
- No explicit calls to fopen/fclose
- Initializing
f {"name-of-file"}opens the file - When
f's scope ends, the file is closed
Try cat in C++
#include <iostream>
using namespace std; // Avoids having to say std::
void echo (istream f) {
char c;
f >> noskipws;;
while (f >> c) {
cout << c;
}
}
int main(int argc, char *argv[]) {
if (argc == 1) {
echo(cin);
} else {
for (int i = 1; i < argc; ++i) {
ifstream f{argv[i]};
echo(f);
}
}
}Doesn't work, won't even compile!
cin has type istream - echo takes an istream
f has type ifstream - is echo(f) as type mismatch?
No! - this is actually fine, ifstream is a subtype of istream
Any ifstream can be treated as an istream
- Foundational concept in OOP
- details later
The error is: you can't write a function that takes an istream the way that echo does
Why not?
Compare:
int x;
scanf("%d", &x);vs
int x;
cin >> x;C and C++ are pass-by-value languages, so scanf needs the address of x in order to change x via pointers
So why is it not cin >> &x?
C++ has another small pointer-like type
int y = 10
int &z = y; zis an lvalue reference toy- Similar to
int *const z = y;but with auto-dereferencing
z = 12;
NOT *z = 12;
y is now 12
int *p = &z; // Gives the address of y
In all cases, z acts as if it were y
z is an alias ("another name for y")
lvalue references must be initialized to something that has an address
int &z = 4; WRONG
int &z = a + b; WRONG
- Cannot create a pointer to a reference:
int &*x; - Read right to left:
-
x is a pointer to a reference of an int
-
- However you can create a reference to a pointer:
int *&x = __; - Create a reference to a reference:
int &&r = z;(compiles but means something else) - Create an array of references:
int &r[3] = {...}; - You can use as function parameters:
void inc(int &n) {
++n;
}
int x = 5;
inc(x);
cout << x; // 6Note: cannot use inc(5) because references require objects with addresses (ie. literals do not)
cin >> x works because x is passed by reference.
istream& operator >> (istream &in, int &n);
Now consider struct ReallyBig {...};
int f(ReallyBig rb) {
...
}Note: struct ReallyBig is not necessary in C++ unlike in C
The case above uses passes by value which copies the huge struct -> this is really slow!
Instead let's pass by reference which doesn't copy it -> this is fast! But this can be dangerous as the function may propogate changes to the caller.
int g (ReallyBig &rb) {
...
}So finally we can use constant references, this is fast and safe!
int h(const ReallyBig &rb) {
...
}Prefer pass-by-const-ref over pass-by-value for anything larger than a pointer, unless the function needs to make a copy anyways.
Also:
int f(int &n) {
...
}
int g(const int &n) {
...
}f(5) - (BAD) can't initialize an lvalue reference n to a literal value 5.
g(5) - (GOOD) since n can never be changed, the compiler allows this (stored in some temp location so n has something to point to)
Back to cat:
void echo(istream f) {
...
}fis passed by value,istreamis copied- Copying streams is not allowed (C++ has mechanisms to prevent copying)
Works if you pass the stream by reference:
void echo (istream &f) {
f >> noskipws;
char c;
while (f >> c) {
cout << c;
}
}To compile:
g++ -std=c++14 -Wall mycat.cc -o mycat- OR
g++14 mycat.cc -o mycat ./mycat
Put echo in its own module
void echo (istream &f);
#include "echo.h"
void echo(istream &f) {
...
}#include <iostream>
#include <fstream>
#include "echo.h"
int main(...) {
echo(cin);
...
echo(f);
}
Compiling separately:
g++14 echo.cc(fails)- linking error: no main
g++14 main.cc(fails)- linking error: no echo
Correct:
g++14 -c echo.cc -> creates echo.o
g++14 -c main.cc -> creates main.o (these are object files)
-c indicates only compile, don't link
g++14 echo.o main.o -o mycat (linker)
Advantage:
- Only have to recompile the parts you change, then relink (no so expensive)
- Ex. Change echo.cc -> recomplie echo.cc -> relink
- However if you change echo.h, you must recompile
echo.ccandmain.ccand relink- This is because both files include
echo.h
- This is because both files include
- What if we don't remember what we changed or what depends on what?
- Linux tool:
make - Create a Makefile
- Linux tool:
mycat: main.o echo.o
g++ main.o echo.o -o mycat
main.o: main.cc echo.h
g++ -std=c++14 -Wall -c main.cc
echo.o: echo.cc echo.h
g++ -std=c++14 -Wall -c echo.cc
.PHONY: clean
clean:
rm mycat main.o echo.oNote: cannot use aliases, requires TABS not SPACES
- targets: mycat, main.o
- dependencies: everything right of colon
- recipes: tabbed information
How make works: list a dir in long form ls -l
-rw-r----- 1 j2smith j2smith 25 Sep 9 15:27 echo.cc
- Based on last modified time
- Starting at the leaves of the dependency graph
- if the dependency is newer than the target, rebuild the target ... and so on, up to the root target
ex. echo.cc newer than echo.o -rebuild echo.o echo.o newer than mycat - rebuild mycat
Shortcuts - use variables:
CXX = g++ (name of compiler)
CXXFLAGS = -std=c++14 -Wall
EXEC = myprogram
OBJECTS = main.o echo.o
${EXEC}: ${OBJECTS}
${CXX} ${OBJECTS} -o ${EXEC}
main.o: main.cc echo.h
echo.o: echo.cc echo.h
.PHONY: clean
clean:
rm ${OBJECTS} ${EXEC}*Omit recipes - make "guesses" the right one
Writing the dependencies still hard (but compiler can help).
g++14 -c -MMD echo.cc: generatesecho.o, echo.dcat echo.d->echo.o echo.cc echo.h
CXX = g++
CXXFLAGS = -std=c++14 -Wall -MMD
EXEC = mycat
OBJECTS = main.o echo.o
DEPENDS = ${OBJECTS:.o=.d}
${EXEC}: ${OBJECTS}
${CXX} ${OBJECTS} -o ${EXEC}
-include ${DEPENDS}
.PHONY: clean
clean:
rm ${EXEC} ${OBJECTS} ${DEPENDS}