Lecture 3, 1/14/13
In the actual code, make sure you understand, or ask about:
This lecture we should
enscript --color=1 -C -Ecpp -1 -o nim.ps nim.cpp ps2pdf nim.pscan do with what you get from hilite.me. By the way,
Nano | Vi | Emacs | Netbeans etc. | CVE | |
---|---|---|---|---|---|
Pro |
easy for novices commands visible | most widely deployed UNIX editor | most powerful | awesome nav + libraries help | virtual office hours |
Con |
designed for email, not programming up to 5/24 of console wasted | insert vs. command mode | learning curve | huge, not on wormulon, scp | buggier than a south texas dumpster dive |
Dr. Rinker sez: Homework #1 and Lab #2 will most likely get posted later this afternoon! Check back on the web then.
Class then proceeded with Quiz #1 and a hand-simulation
will tell emacs to show you the current column number at all times, along
with the line number that it was already showing.
Debugging
To "debug" means to "correct" or "fix" a program that isn't working
correctly. Many ways to do it:
Following the Locus of Execution in the Debugger GDB
More C++
Language features we should discuss:
Main ideas you
are supposed to get:
Today's C++ Concepts
for
loop
Log Off Cleanly
Check out the UI CS FAQ.
Have they told ya about /x/ and X:
The FAQ tells me that you have an extra Gigabyte out there...
Quick Tour of C/C++ Operator List
In case we missed any. Where do << and >> appear on this list?
C++ Practice
Suppose you are asked to write a program to "read in a series of numbers".
(Maybe you are going to add them up, average them, or whatever).
What C++ features would you need to use?
The Great Function Lecture
Emacs Tip
Need to know what column you are on?
ESC-x column-number-mode
Need Indentation Help ?
Try GNU indent(1). Demo in class. I seem to prefer
indent -bap -bbb -br -blf -bli3 -cdw -l79 -npcs -nprs -npsl
which you could easily define an alias short-cut for. But I would be
willing to put up with most of the available COMMON STYLES.
A Few Rules of Floating Point and Integer Arithmetic
Examples:
Local Variables vs. Global Variables
The distinction becomes important once multiple functions are used.
Parameters
lecture #9
if ((x==2)||(x==3)||(x==5)||(x==7))But with a switch, you can write
case 2: case 3: case 5: case 7:
#include <iostream> using namespace std; void f(int); int y; int main() { int x = 6; y = 6; f(x); cout << "x should be 7; it is ", << x << endl; } void f(int p) { p = p+1; }If I asked you what was wrong, you might ask the following questions:
f(x); f(x+6); f(73);the body of f() will execute three times, with p having different values each time.
int f();
with no parameter type?
cin >> x;
?
void f(int);
be inside main's body?
x = f(x); ... int f(int p) { p = p+1; return p; }
int main() { cout << "hey" << endl; LABEL: x = get_value(); ... if (t) { x = x + 2; goto LABEL; } }
if (x == SENTINEL) cout << "Amazing, this is so rare!" << endl;and its cousin
while (x < SENTINEL); cout << x++ << endl;
return
is not a function
return(x);
? Clearer as
return x;
x+1;it runs, but does nothing.
longwindedvariable = longwindedvariable + 1;looks better as:
longwindedvariable++;note for adding something other than one:
longwindedvariable += 5;
Some of you are commenting very nicely, one or two assignments have been overcommented, but many of you are not commenting much, and we will need to work on that. This assignment will have a grading point for comments.
Implications:
#include "foo.cpp"
allowed
double divide(double num, double den) { if (den == 0.0) { cout << "Can't divide by 0" << endl; } return num / den; }
return
statement.
after the midterm exam, a class devoted to going over midterm answers ...
ifstream
#include <fstream>
open()
and close()
open()
can fail
<<
) can fail
fail()
member function, or check
the value produced by evaluating the input operator or function.
eof()
member function.
It means: there is no more input, and this is not really an error.
.c_str()
method
clear()
clear()
clears any error condition from a past operation.
ignore()
ignore(limit, stopchar)
discards (up to limit
)
characters until it hits a stopchar
.
ofstream
open()
and close()
open()
can fail
#include <iostream> using namespace std; int f(int p[], int arraylen) { for(int i = 0; i < arraylen; i++) p[i] *= p[i]; return p[0]+p[1]+p[2]+p[3]+p[4]; } int main() { int a[5] = {1,2,3,4,5}; int answer = f(a, 5); cout << " Answer " << answer << " middle-element " << a[2] << endl; } |
#include <iostream> using namespace std; class a { public: int u,v,w,x,y; a() { u=1; v=2; w=3; x=4; y=5; } }; int f(a b) { b.u *= b.u; b.v *= b.v; b.w *= b.w; b.x *= b.x; b.y *= b.y; return b.u+b.v+b.w+b.x+b.y; } int main() { a aa; int answer = f(aa); cout << " Answer " << answer << " middle-element " << aa.w << endl; } |
Lessons from these fine programs:
int n; count << "How many? "; cin >> n; int a[n]; for (int i=0; i<n; i++) cin >> a[i];
The rest of this discussion focuses on time. Proportional to input size (n) means literally: n comparisons, n inputs, n increments. If the work per element (1 compare, 1 input, 1 increment) adds up to some time c, our total time is cn, a co-efficient constant times the number of times around the loop.
Here is another example: how much time will it take to determine whether all n integers are sorted in ascending order?
boolean is_ascending(int a[], int n) { for (int i=0; i<n-1; i++) if (a[i]>a[i+1]) return 0; return 1; }The time is: you guessed it, proportional to n. But this problem doesn't always take n. In the best case, it returns on the first "if". On the average case (if the data is random) it takes far, far less than n to figure out that the answer is false. In the worst case it takes n.
Here's a tougher one: how much time will it take to determine whether any of the numbers are duplicates?
boolean is_unique(int a[], int n) { for (int i=0; i<n; i++) for (int j = 0; j<i; j++) if (a[i] == a[j]) return 0; return 1; }This is n times... something. n times we do another loop. The other loop goes i times, but i is bounded by n and averages n/2. The total number of times through the for-loops is n*n/2 or n2/2.
struct
types '\0'
) is the NUL byte that ends a string
int A[6][7];See array.cpp for a short example. lecture #23
(*op).f();The * followed by . pattern was recognized early on as being so common that it got its own, more readable operator:
op->f();
for (int i = 1; i < argc; i++) cout << "arg" << i << " is " << argv[i] << endl;
#include <iostream> using namespace std; int main(int argc, char *argv[]) { for (int i = 1; argv[i]!=NULL; i++) cout << "arg" << i << " is " << argv[i] << endl; }
new
and delete
which are Very important.
Are you ready for Wednesday's quiz?
This lecture on base conversions and two's complement arithmetic is provided courtesy of Dr. Rinker.
(IJKL)Bcan be evaluated in the following way:
L * B0 + K * B1 + J * B2 + I * B3
Decimal | Binary | Octal | Hexadecimal |
---|---|---|---|
0 | 00000 | 00 | 0x00 |
1 | 00001 | 01 | 0x01 |
2 | 00010 | 02 | 0x02 |
3 | 00011 | 03 | 0x03 |
4 | 00100 | 04 | 0x04 |
5 | 00101 | 05 | 0x05 |
6 | 00110 | 06 | 0x06 |
7 | 00111 | 07 | 0x07 |
8 | 01000 | 10 | 0x08 |
9 | 01001 | 11 | 0x09 |
10 | 01010 | 12 | 0x0A |
11 | 01011 | 13 | 0x0B |
12 | 01100 | 14 | 0x0C |
13 | 01101 | 15 | 0x0D |
14 | 01110 | 16 | 0x0E |
15 | 01111 | 17 | 0x0F |
16 | 10000 | 20 | 0x10 |
1101 = 1 * 23 + 1 * 22 + 0 * 21 + 1 * 20 = 1 * 8 + 1 * 4 + 0 * 2 + 1 * 1 = 8 + 4 + 0 + 1 = 13 a.k.a. (13)10
E42 = E * 162 + 4 * 161 + 2 * 160 = 14 * 256 + 4 * 16 + 2 * 1 = 3584 + 64 + 2 = 3650
Method: divide by two repeatedly until you get to 0. Each division by 2 computes one bit (the remainder).
Quotient | Remainder | Accumulated Bits | |
---|---|---|---|
25/2 | 12 | 1 | 1 |
12/2 | 6 | 0 | 01 |
6/2 | 3 | 0 | 001 |
3/2 | 1 | 1 | 1001 |
1/2 | 0 | 1 | 11001 |
Method: divide by 16 repeatedly until you get to 0. Each division by 16 computes one hex digit (the remainder).
Quotient | Remainder | Accumulated Hex Digits | |
---|---|---|---|
79/16 | 4 | 15 | F |
4/16 | 0 | 4 | 4F |
Method: every four bits of binary is exactly 1 hex digit. Convert each four bits to decimal and then map to hex.
binary | 1010 | 1110 |
---|---|---|
decimal | 10 | 14 |
hex | A | E |
answer | AE |
Method: every hex digit is exactly four bits of binary. Convert each hex digit to its decimal equivalent if you need to, but then use it to fill out a four-bit binary value.
initial hex value | 3B7 | ||
---|---|---|---|
hex | 3 | B | 7 |
decimal | 3 | 11 | 7 |
binary | 0011 | 1011 | 0111 |
answer | 001110110111 |
+57 |
| ||||||||
---|---|---|---|---|---|---|---|---|---|
-57 |
|
number | its decimal |
---|---|
00101101 | 45 |
11101101 | -(00010010), or -18 |
11111110 | -(00000001), or -1 |
number | its one's complement |
---|---|
00101101 | 11010010 |
11101101 | 00010010 |
The leftmost example shows the carry bits, which are often
omitted. The rightmost example would require 9 bits to
represent. If stored in a char
variable, the
result would be an numeric overflow and loss of data; the
resulting 8-bit number is incorrect. If stored in a larger
integer type, no overflow would occur
1111 00101110 01000111 10110110 + 00011101 + 01101101 + 11011100 ---------- -------- -------- 01001011 10110100 110010010
01011011 10110010 - 00110101 - 00011100 -------- -------- 00100110 10010110
3B4 1AF + 6A5 + 345 --- --- A59 4F4
3B4 5D1 - 195 + 1D8 --- --- 21F 3F9
0010 1101 (45) 0110 1101 (109) + 0001 1011 +(27) + 1111 0010 (-14) ---- ---- ---- ---- 0100 1000 (72) 0101 1111 ( 95)After all these years, it still seems kind of eery to me that adding a negative to a positive is just binary addition.
0010 1001 +(41) 0010 1001 -0001 0111 -(+23) +1110 1001 ---- ---- ---- ---- 0001 0010 (+18)
1110 1010 (-22) 1110 1010 -1101 1000 -(-40) +0010 1000 ---- ---- ---- ---- 0001 0010 (+18, carry bit discarded)
char c2 = 209; cout << "C2 is "<< (int)c2 << endl;...prints out "C2 is -47"
/* find and return all the vowels in s. * what is wrong with this picture? */ char * finder(char *s) { char a[80]; int i=0; for(; *s != '\0'; s++) { if (i<80 && ((*s=='a') || (*s=='e') || (*s=='i') || (*s=='o') || (*s=='u'))) { a[i++] = *s; } } return a; }
class Weather { int spring, summer, fall, winter; int *current; public: Weather(); int gettemp(){ return *current; } void next_season(); }; Weather::Weather(){ spring = 40; summer = 80; fall = 70; winter = 20; current = &spring; } void Weather::next_season() { if (current == &spring) current = &summer; else if (current == &summer) current = &fall; else if (current == &fall) current = &winter; else current = &spring; }
class foo { int x; foo *next; }
next
to the node after, then
assign the node in front's next
to point at the new node
next
to point at the node
after the node to delete
Here is a slight expansion of that discussion
get_next()
and get_data()
are
not really evil; the client code can access a node's fields but is not able to
mess them up.
get_next()
is the most disgusting as field
next
is internal to the logic of the linked list
and none of our business.
get_next()
used? Once, in
linkedlist.cpp, when unlinking from the front of the list. Could you get
rid of it? Well, you could write
void unlink_me(node * &pred) {pred = next;}...so I did.
set_next()
and
set_data(), as they allow anywhere in the program
to mess up any node's fields, making them in effect public.
set_next()
and set_data()
are used once, in
linkedlist::insert(r)
. They
can easily be
removed if the constructor initializes its fields, as in
node::node(robot *r, node *tail){ // constructor next = tail; data = r; }...so my version does that, too.
And now, for something completely different.
Skipping the task of Optimcal Character Recognition (OCR) this amounts to approximately the following data:
Title Author Subject Ph.D. Dissertation Jeffery Execution Monitoring 2009 Al-Sharif Debugging 2011 Bani-Salameh Social Networking The Synthesizer Generator Reps Programming Environments Computer-Aided Reasoning Kaufmann Artificial Intelligence Operating Systems Concepts Davis Operating Systems Handbook of Programming Languages Programming Languages Handbook of Programming Languages Programming Languages Handbook of Programming Languages Programming Languages Symposium on Semantics Programming Languages Definition of Programming Languages Ollengren Programming Languages User-oriented Computer Languages Klerer Programming Languages Computer-Aided Reasoning Kaufmann Artificial Intelligence Object-Oriented Programming Franz Object-Orientation Ada an Advanced Introduction Gehani Ada Software Engineering with Ada Booch Ada Introduction to Algol Baumann Algol Algol 60 Programming Dijkstra Algol Introductory Algol 68 Programming Brailsford Algol ALPHARD: Form and Content Shaw Alphard Learning to Program with Alice Dann Alice 80386 Programmer's Reference Manual Assembler A Programmer's View of the Intel 432Organick Assembler Standard BASIC Programming Catlin BASIC Instant BASIC Brown BASIC Complete BASIC Programming Mandell BASIC Let's Learn BASIC Schneiderman BASIC On to C++ Winston C++ Classic Data Structures in C++ Budd C++ The C++ Programming Language Stroustrup C++ Problem Solving in C++ Shiplet C++Things you can observe trivially
#include <iostream> using namespace std; int main() { Insert Your Ideas Here }Actually, this is the wrong approach. Don't just start coding. Formulate a plan. Start with the data representation. What classes, and what structures, do you need in order to represent my bookshelf? Last lecture, we got a couple .h files and a main() function written... notice that I (lazily) included main() inside bookshelf.cpp. No problemo. Except now I could not easily link bookshelf.o into another program that needed its services. Better for main() to live in its own module. So I fixed that here. And I filled in a few obvious parts in order to expedite our conversation: namely, file I/O and (blank) implementations of member functions.
int factorial (int n) { if (n <= 1) return 1; else return n * factorial(n - 1); }
Here is another, nauseatingly repeated example in which the function is computed from two smaller N's:
int fibonacci (int n) { if (n <= 1) return 1; else return fibonacci(n-1)+fibonacci(n-2); }Although the code is intensely short, it is spectacularly inefficient. Why?
void forloop(int n, int ender) { // basis case if (n <= ender) { // do one iteration of the loop ... // recursive forloop(n+1, ender); } }
void node::print(){ data->print(); cout << endl; if(next != NULL) next -> print(); }
void quickSort( int a[], int l, int r) { int j; if( l < r ) { // divide and conquer j = partition( a, l, r); quickSort( a, l, j-1); quickSort( a, j+1, r); } }
000000001001000 ( 72 dec) 000001000011111 ( ?? dec) --------------- 000000000001000
x | y
000000001001000 ( 72 dec) 000010000011111 ( ?? dec) --------------- 000010001011111
~x
000000001001000 ( 72 dec) --------------- 111111101101111
x ^ y
00000001001000 ( 72 dec) 00001000001111 ( ?? dec) -------------- 00001001000111
tonum(c)which converts c to the corresponding digit. Except hey btw, we need to convert 'A'-'Z' or 'a'-'z' to 10-35 to handle bases 11-36, which Unicon supports.
We don't (but could) define tonum(c) like this:
int tonum(int c) { if (('0' <= c) && (c <= '9')) return c - '0'; else if (('A' <= c) && (c <= 'Z')) return c - 'A' + 10; else if (('a' <= c) && (c <= 'z')) return c - 'a' + 10; }Instead, we define a macro and use bitwise AND:
#define tonum(c) (isdigit(c) ? (c - '0') : ((c & 037) + 9))And in order to explain that, we need to
<ctype.h>
Suppose you are an 'F'.
1 << 5
does?
Before there ever was a "send" operator to cout
,
there was a "left shift" operator that slides bits around.
And 213016 >> 5
?
Consider shift.cpp
class BasketballTeam { Center c; Forward f1, f2; Guard g1, g2; }; |
class BasketballTeam { Center *c; Forward *f1, *f2; Guard *g1, *g2; }; |
class BasketballTeam { Player players[15]; Coach c; };
class BasketballTeam { Player *players; // linked list of players Coach c; };Either way, for the team to pick out an individual player, it may need to search through its collection.