Nothing Special   »   [go: up one dir, main page]

A C++ Crash Course: UW Association For Computing Machinery

Download as ppt, pdf, or txt
Download as ppt, pdf, or txt
You are on page 1of 57

A C++ Crash Course

Part I

UW Association for Computing Machinery


http://www.cs.washington.edu/orgs/acm/tutorials
acm@cs.washington.edu

Questions & Feedback to Hannah C. Tang (hctang) and Albert J. Wong (awong)
What We’ll Cover
• C/C++ fundamentals • C-style types
– Functions – Typedefs
– Primitive data types – Structs

• The stack • Arrays (whole story)


– Arrays (working model)
– Pointers
• More C++-isms
– C++-style vs Java-style
• A practice program references
– C++ gotchas
What We’re NOT Covering
• Topics related to C++ classes
– Multiple files
– The preprocessor

• C++ classes
– Inheritance and dynamic dispatch

• Memory management
– The heap
– Destructors

• Advanced topics
– Modifiers: const, static, and extern
– Operator overloading
– Templates
Goals of Java
Java, C, and C++, have different design goals.

Java
– Simple
– Consistent
– Huge OO Focus
– Cross platform via a virtual machine
– Originally for embedded systems
Goals of C and C++
C and C++ are popular because they have met, with
reasonable success, their goals.
C
– Low level
– No Runtime Type info
– Easy implementation

C++
– Originally to add some OO functionality to C
– Attempt to be a higher-level language
– Now it’s a totally different language
A simple program snippet
public void printSum(void) void printSum(void)
{ {
int x, y; int x, y;

// … get user input … // … get user input …

int sum = x + y; int sum = x + y;

// … print sum … // … print sum …


} }
The simple program – Java version
class Calculator {
public void printSum(void) {
int x, y;

// … get user input … App


int sum = x + y;

// … print sum …
} printSum()
}

class App {
public static void main(String[] args) { Calculator
Calculator c = new Calculator;
c.printSum();
}
}
The simple program – C++ version
void printSum(void) { class Calculator {
int x, y; public void printSum(void) {
int x, y;
// … get user input …
// … get user input …
int sum = x + y;
int sum = x + y;
// … print sum …
} // … print sum …
}
}

int main(int argc, class App {


const char * argv[]) { public static void main(String[] args)
printSum(); {
return 0; Calculator c = new Calculator;
} c.printSum();
}
}
Procedural Programming
• Functions are free-floating “methods” disassociated from
any class

• Functions declarations can be separate from function


implementations
– The declaration provides the signature, which specifies the
name, return type, and parameter list of the function

• C is completely procedural

• C++ mixes object-oriented and procedural programming


Discussion Point I
Which of these programs can be written
procedurally? Object-orientedly?
• HelloWorld
• A traffic simulator
– Must simulate cars, roads, and the interactions between
these entities
• A calculator
– Accepts two numbers, then calculates the sum or
difference, depending on a user-selected operator
• An mp3 player
– Accepts a list of files, and plays them in the specified
order. Needs to support skins
• Come up with your own example
Function Syntax and Semantics
<ReturnType> functionName( … <parameter list> … );

int calculatePower(int base, int exponent);

• <ReturnType> can be any type except an array


• Class-scoped methods and free-floating
functions are basically the same, except …
Parameter Passing in Java – Part I
class Example {

public void moveToDiagonal(Point p) {


p.setY(p.getX());
}

public static void main( String[] args ) {


Point pt;
pt = new Point(3, 4);
moveToDiagonal(pt);
// What are the coordinates of pt now?
}
}
In Java, everything is a reference

x: 3 x: 3
y: 4 y: 3

pt p

Point pt; moveToDiagonal(Point p) {


pt = new Point(3, 4) p.setY(p.getX());
}

In Java, modifying a method parameter means


modifying the original instance
… almost everything is a reference
Java atomic types: C++ atomic types:
• int • int
• double • double
• boolean • bool
• etc … • etc …

In Java, modifying an atomically-typed parameter did


NOT modify the original instance.

In Java, atomic types are passed by copy. The same


semantics hold for C++ atomic types
C/C++ Function Parameters
• In C++, all function parameters are passed
by copy – even if they’re not of atomic type
• Why?
– First, a brief detour …
Detour: Functions & Memory
• Every function needs a place
to store its local variables.
Collectively, this storage is i
called the stack Memory
location d2
• This storage (memory aka
“RAM”), is a series of storage d1
spaces and their numerical y
addresses
• Instead of using raw x
addresses, we use variables
to attach a name to an
address void aFunc(int x, int y)
• All of the data/variables for a {
double d1, d2;
particular function call are int i;
located in a stack frame }
Detour: Functions & Memory (cont)
• When a function is called, a new
stack frame is set aside
• Parameters and return values are
passed by copy (ie, they’re copied
into and out of the stack frame)
• When a function finishes, its stack
frame is reclaimed

void aFunc(int x, int y) {


double d1 = x + y;
} d1
int main(int argc,
y aFunc
const char * argv[]) {
int x = 7;
x
aFunc(1, 2);
aFunc(2, 3); x 7 main
return 0;
}
C/C++ Function Parameters (cont.)
• In C++, all function parameters are passed by
copy – even if they’re not of atomic type

• Why?
– In C++, all variables exist on the stack by default
– In C++, parameters are copied into the callee’s stack
frame
– We’ll talk about Java parameter passing later (when
we talk compare C++ and Java references)
Discussion Point II
• Examine the code fragment below.
– Draw the stack frame(s) for some sample input.
– If you see any bugs, what are they? How would the
program behave?

void sillyRecursiveFunction(int i) {
if(i == 0) {
return;
}
else {
sillyRecursiveFunction(i – 1);
}
}
Arrays
<ArrayType> arrayName[ numElements ]

• Arrays are contiguous memory myArray[5]


locations, and its name refers
only to the address of the first myArray[4]
element
myArray[3]
• Indexing into an array is the
same as adding an offset to the myArray[2]
address of the first element
• When declaring an array, its size myArray[1]
must be known at compile-time myArray[0]
or myArray
Arrays as function parameters
<ReturnType> funcName( ArrayType arrName[ ] )

int sumOfArray( int values[], int numValues )

• Arrays are not passed by copy. Instead,


the address of the first element is passed
to the function
– Note how array parameters and non-
parameter arrays behave identically
Discussion Point III
• Why are arrays not passed by copy?
– Hint: the size of a stack frame is computed
long before the program is run (specifically, at
compile time)
Pointers
What if we had variables that contained addresses?
They could contain addresses of anything!

x
We could use these (4104)
variables in functions to
modify the caller’s data y
(4100)
(we could implement Variable
Java’s parameter- name n
passing semantics!) (4096)

Address Storage space


Pointers: vocabulary
• A pointer is a variable
which contains
addresses of other x
(4104)
variables
• Accessing the data at y
(4100) 4096
the contained address
is called “dereferencing n
7
a pointer” or “following (4096)
a pointer”
Pointer Syntax
Declaring Pointers Using Pointers
Declaring a pointer: Dereferencing a pointer:
<Type> * ptrName; *ptrName
“Go to the address contained in the
“ptrName is a variable which variable ptrName”
contains the address of
something of type <Type>” Getting the address of a variable:
&aVar
“Get the address of aVar”

For example:
For example:
int * nPtr1, * nPtr2;
aFunc(myInt,
void aFunc( &anotherInt);
int aParam,
int * ptrParam); anInt = *myPtr * 4;
*dinner = 100;
Pointers: Putting it all together
The code Box Diagrams Memory Layout

p contains the
int * p; “p’s type is int
address of an
int q; pointer. q’s type
int. q contains an
is int.”
int.
p = &q Go to the address
“Assign 5 to where that p contains, and
*p = 5;
p points (which is place a 5 there.
q).”

p (8200) 8196
p q 5
q (8196) 5
Pointers: Putting it all together (cont.)

The code Box diagram Memory Layout

void doubleIt(int x, main


int * p)
p 8192
{ a 16 (8200)
*p = 2 * x; doubleIt
} x 9
int main(int argc, (8196)
const char * argv[])
a 16 main
{ doubleIt (8192)
int a = 16;
doubleIt(9, &a); x 9
return 0;
} p
Pointer Arithmetic
Pointers are numbers, so you can do math on them!

int * p = &a; *p = 200; *(p+1) = 300;

p p p
(8200) 8192 (8200) 8192 (8200) 8192
b b b
(8196) 9 (8196) 9 (8196) 300
a a a
(8192) 16 (8192) 200 (8192) 200

Pointer p refers to an int, so adding 1 to p increments the address by


the size of one int. The C/C++ expression for this is sizeof(int)
Pointers and Arrays
Pointers and arrays are (almost) interchangeable
Given: myArray[4]
(9000)
int myArray[5];
myArray[3]
int * p = myArray; (8196)
myArray[2]
These are equivalent: (8192)
• *p myArray[1]
• myArray[0] (8188)
• *(p+0) myArray[0]
• *myArray (8184)
• p[0] p
• 0[p] 8184
(8180)
Discussion Point IV
• How do pointers and arrays differ?
– Hint: how are pointers implemented in
memory? Arrays?
Exercise
• Get up and stretch!
• Do the worksheet exercise
• Then, write a program to do the following:
– Read some numbers from the user (up to a
max number of numbers)
– Calculate the average value of those numbers
– Print the user’s values which are greater than
the average
• Get up and stretch again!
Pointer Problems
• Pointers can refer to other variables, but:
– Create an additional variable
– Have an ugly syntax


Function Pointers
<ReturnType> (*ptrName)(arg type list );
• Functions are pieces of code in memory
• Pointers can point to functions.
• This syntax is U-G-L-Y (the ugliest in C)
• Notice that the name of the variable appears in
the middle of the statement!
• You do not have to dereference a function
pointer

Function pointers are not scary. They are useful!


Function Pointers - example
void foo(int i, char b);
void bar(int i, char b);

int main(void) {
void (*p)(int,char);

p = foo;
p(1, ‘c’); // equivalent to foo(1, ’c’);

p = bar;
p(2, ‘b’); // equivalent to bar(2, ‘b’);
(*p)(2, ‘b’); // Exactly the same

return 0;
}
References
References are an additional name to an
existing memory location

If we wanted something called “ref” to refer to a variable x:

Pointer: Reference:

x 9 x
9
ref

ref
Properties of References
Reference properties:
– Cannot be reassigned
– Must be assigned a referee at construction

Therefore:
– References cannot be NULL
– You cannot make an array of references.

Given what you know about references, can you


explain where these properties come from?
Reference Syntax
References Pointers
Declaring a reference: Declaring a pointer:
<Type> & refName = <Type> * ptrName;
referee;

Usage: Usage:
int n; int n;
int & referee = n; int * nPtr1 = &n;
void aFunc( void aFunc(
int aParam, int aParam,
int & ptrParam); int * ptrParam);

aFunc(1, n); aFunc(1, &n);


Discussion Point V
• What are the differences between Java
references and C++ references? What
about Java references and C++ pointers?
C-style struct
A struct is used to group related data items
struct student {
int id;
char name[80;] Note that the it is optional
}; to name a struct

• To the programmer
– id and name are now related
– struct student creates a convenient grouping

• To the compiler
– Id and name have a fixed ordering (not offset) in memory
– Struct student is a first-class type that can be passed to functions
struct Syntax
Declaring a Struct Access struct fields
Declaring a struct: Accessing a field in a struct:
struct [optional name] { foo.field1;
<type> field1;
<type> field2; “gets field1 from the instance foo of
… struct Foo”
} [instance list];
Pointers syntax and structs
Examples: The * has lower precedence than the ‘.’ :
struct Foo { *foo_ptr.field1;
int field1; means
char field2; *(foo_ptr.field1);
} foo,*foo_ptr; Which won’t compile
struct Foo foo2; Accessing a field in a struct pointer:
(*foo_ptr).field1;
struct { int a; } blah;
foo_ptr->field1;
enum
An enum creates an enumerated type; they
are options with an associated value
enum PrimaryColors {
RED = 0,
GREEN,
BLUE Note that the it is optional
to name an enum
};

• By default, the first option is given the value 0


• You can assign an option any integer
• Subsequent options have the previous option’s value + 1
• All enumeration values are in the same namespace
enum Syntax
Declaring an enum Enum quirks
Declaring a enum:
enum [optional name] { Problems with Enums:
OptionName [= int], • Frail abstraction
OptionName [= int], • Treated as integers
… • Can be assigned invalid values
} [instance list]; • Flat namespace

Example of an enum: Proper use guidelines:


enum Color { • Avoid breaking abstraction
RED, • Mangle name of enum into option
GREEN, name (so ColorRed instead of Red)
BLUE
} color, *color_ptr; Here is one sanctioned abstraction break
enum Color {
enum Color c; RED,
GREED,
void drawCircle BLUE,
(enum Color c); NumberOfColors
};
union
An union creates an union type; all fields
share the same memory location
union Argument {
int intVal;
double doubleVal;
char charVal; Note that the it is optional
}; to name a union

• Changing intVal changes doulbeVal and charVal!


• Can be used to create constrained-type containers
• Usually used in conjunction with an enum that says which
field is currently valid.
union Syntax
Declaring an enum Union quirks
Declaring a enum:
union [optional name] { Problems with Enums:
<type> name1; • Only assume that the last field
<type> name2; written two is valid.
• Don’t use to “save space.”

} [instance list];
Proper use guidelines:
Example of a union: • Ensure you have another method
of knowing which field is currently
union Argument { valid.
int value;
char *string;
} arg1, *ptr;

union Argument arg2;

arg1.value = 3;
arg2.string = NULL;
Typedef
Typedef is used to create an alias to a type
typedef unsigned char byte;
unsigned char mybyte;
byte mybyte;

• byte now represents an unsigned char

• Both definitions of mybyte are equivalent to the compiler.

• The second definition is preferred as it gives more info


Typedef – common uses
• Abstraction
– The user may easily change the type used to represent
a variable.

• Clarification
– More informative names for a type be given
– Variables that use the same type in different ways can
be separated easily

• Convenience
– Type names can get very long
– People like structs to look like real types
– Some type names (like function pointers or array
pointers) are really hard to read/write
Typedefs – structs/enums/unions
People often make a typedef of an
anonymous struct, enum, or union
typedef struct { struct Student {
int id; int id;
char name[80]; char name[80];
} Student; };

Student st; struct Student st;

These are almost the same. struct List {


However, anonymous int data;
structs cannot refer to struct List *next;
themselves. };
Discussion Point VI
• What advantages do named structs/unions
have over anonymous ones? Are enums
different?
– How would you try to pass anonymous
structs, enums, or unions to a function? Can
you?
C++ “Gotcha” I
Don’t use exceptions unless you
know what you’re doing!
• Uncaught C++ exceptions do not produce a stack trace.
• C++ does not automatically reclaim new’d resources
(more in a later tutorial)
void someFunc(void) { $ ./myProg
throw “Exception!"; Aborted
} $
int main(int argc,
const char * argv[]) {
someFunc();
return 0;
}
C++ “Gotcha” II
Don’t return pointers (or references) to
local variables!
double * aFunc(void) {
double d;
return &d;
}
int main(int argc,
const char * argv[]) {
double * pd = aFunc();
*pd = 3.14;
return 0;
}
Boom! (maybe)
C++ “Gotcha” III
Uninitialized pointers are bad!
int * i;

if( someCondition ) {

i = new int;
} else if( anotherCondition ) {

i = new int;
}
Does the phrase “null
pointer exception”
*i = someVariable; sound familiar?
C++ “Gotcha” IV
Never use an array without knowing its size
int myArray[5];
• C++ arrays do not
know their own size. myArray[0] = 85;
myArray[1] = 10;
– Always pass a size myArray[2] = 2;
variable with the array myArray[3] = 45;
– Always check the myArray[4] = 393;
bounds manually (C++
won’t do it for you!) myArray[5] = 9;
myArray[-1] = 4;

No Error! Undefined Behavior!


What We Covered
• The procedural programming paradigm

• Functions and parameter passing

• The C/C++ memory model – Part I (the stack)


– Pointers
– Arrays
– C++-style References

• C type constructs
– Structs, enums, unions, typedefs

Any questions?
Acknowledgements & References
– Books:
– Essential C++ (C++ In-Depth Series), Stanley B. Lippman, 1999, 304
pgs.
– The C++ Primer, 3rd edition, Stanley B. Lippman, 1998, 1237 pgs.
– Effective C++, 2nd edition, Scott Meyers, 1997, 304 pgs.
– The C++ Language, 2nd Edition, Bjarne Stroustrup, 2000, 1019 pgs.
– Thinking in C++, 2nd Edition, Bruce Eckel, 2000, 814 pgs.
Also available online (for free):
http://mindview.net/Books/TICPP/ThinkingInCPP2e.html
• Nathan Ratliff
– Version 1 of the C++ tutorial
• Doug Zongker
– Version 1 of the handouts
• Hannah C. Tang & Albert J. Wong
– Wrote, proofread, and presented the current version of the tutorial and
handouts
It’s basically over now
The next few slides are here for completeness. You
do not need to know most of the following info.

The stuff on array, the majority of C developers


probably do not know this following info.

If you are not comfortable with the material on


pointers and arrays presented previously, just skip
the next slides.

If you are terminally curious, keep going.


Arrays (the whole story)
Arrays are not pointers. They are not first
class types either.
• Arrays know their size!
• Arrays forget their size after they get passed to a function!
• You CANNOT return arrays of any type
int foo(int ar[ ]) {
printf(“%d\n”, sizeof(ar)); The output of this, assuming
} a 4-byte int would be:

int main(void) { 40
int ar[10]; 4
printf(“%d\n”,sizeof(ar));
foo(ar);
return 0;
}
Pointers to Arrays
int (*ar)[3] vs. int *ar[3]
• The first is a pointer to an array of 3 integers.
• The second is a array of 3 elements, where each element is an int-
pointer.
• This is how multidimensional arrays work
p
(8200) 8184
int a[3]; p2
(8196) 8184
int *p = a;
p+1 == 8188 &a[2]
(8192) 16
int (*p2)[3] = &a; &a[1] 485
p2+1 == 8196 (8188)
(*p2)[0] == p2[0][0] == 122
&a[0] 122
(*(p2+1))[0] == p2[1][0] == p2 == 8184
(8184)

You might also like