Transcript slides

C++ crash course
Class 4
arrays, pointers, functions
Boolean Logic
Arrays and Pointers redux
Class functions
Finishing the bookstore
Boolean Logic
• Computers deal in 1’s and 0’s
• Many times we want to make decisions in
terms of logic
if( am_hungry AND like_apples) {
Boolean Logic
• C++ (like many other languages) allows you to do this using
logical operators
• These are binary operators, meaning they operate on two
operands (the quantities on either side)
• Results in a bool-type expression
logical and
logical or
logical not
<, >, <=, >=
tests equality
tests non-equality
comparison operators
operate on bool-type expressions only
can operate on many
Boolean Logic
• How do we understand these operators?
– Bool-expressions are either TRUE or FALSE
• &&, ||, ! all change these values
A && B
Boolean Logic
A || B
We started talking about these yesterday
Contiguous allocation in memory, accessed by pointers
Arrays are a compound type
– Need: type specifier, identifier, dimension (must be known at compile-time)
const unsigned buf_size = 512, max_files = 20;
int staff_size = 27;
const unsigned sz = get_size();
char input_buffer[buf_size];
string fileTable[max_files + 1];
int vars[12]; // this works, but is bad practice
// don’t hardcode except in consts
double salaries[staff_size];
int test_scores[get_size()];
int vals[sz];
When we initialize an array, we can provide an initialization for each element
Otherwise, arrays will be initialized as they are in vectors – default variable
const unsigned array_sz = 3;
int ia[array_sz] = {0,1,2};
const unsigned array_sz2 = 5;
int ia2[array_sz2] = {0,1,2};
// this results in {0,1,2,0,0};
Character arrays are special, and can be initialized with character string literals
char ca1[] = {‘C’, ‘+’, ‘+’}; // note: no null terminator!
char ca2[] = {‘C’, ‘+’, ‘+’, ‘\0’}; // null terminated
char ca3[] = “C++”; // automatically null-terminated; equivalent
// to the previous line
• Can’t initialize arrays with other arrays!
int ia[] = {0,1,2}; // OK
int ia2[](ia); // bad
int ia3[3]; // ok, but uninitialized elements
ia3 = ia; // bad
• Arrays CANNOT get bigger once they’ve been created
• Some compilers will let you do it anyway as a compiler
extension, but you’re not guaranteed this
• Operators
– The size of an array is type size_t
– No size function; should be set as a const somewhere
– Subscript operator can change elements (a[n])
• Unlike in other languages, it is PERFECTLY LEGAL
to access an array element outside of the size of
the array!
– Don’t do this: you’re messing with memory you
shouldn’t be. Always check your array size before
• get_size() takes no arguments and returns an
• Which of these are legal definitions?
unsigned buf_size = 1024;
(a) int ia[buf_size];
(b) int ia[get_size()];
(c) ia[4 * 7 – 14];
(d) char st[11] = “fundamental”;
• Which of these definitions are illegal?
(a) int ia[7] = {0,1,1,2,3,5,8};
(b) vector<int> ivec = {0,1,1,2,3,5,8};
(c) int ia2[] = ia1;
(d) int ia3[] = ivec;
• Pointers can also be used for array access
• We saw this yesterday
– Pointers are compound types
– Pointers are the iterators for arrays
• Iterators can only be used for a specific
• Pointers can point to any kind of variable
• * is the dereference operator
– in a variable definition, this indicates that the
variable is a pointer
• & is the address-of operator
– tells us the address of a variable
– Remember this from references?
In a reference, instead of setting the value of a
variable when it’s declared, we set its address –
this is why references are bound once they’re
• We can iterate through an array like so:
const size_t array_sz = 4;
int ia[array_sz] = {0,1,2,3};
for(int *ptr = ia,size_t ctr = 0;
ctr < array_sz;
ptr++, ctr++)
cout << *ptr;
• Why did we need the counter?
• As mentioned, we can point to anything
vector<int> *pvec;
int *ip1, *ip2;
string *pstring;
double *dp;
double dp, *dp2; // only dp2 is a pointer here
• You can also write:
string* ps1, ps2;
• …but that can be confusing, since only ps1 is
getting defined as a pointer
• As always, make sure you initialize your pointers!
• Pointers only hold addresses in memory
• Just like when we fail to initialize ints or other
types with no default constructor (usually basic
types), pointers will try to interpret whatever’s in
memory at the point it’s been assigned as the
address it should be pointing to
• If you don’t know where to point your pointer,
point it at 0 – that way you can find out if it’s
been initialized
• Initialization can happen in four ways:
Constant expression with value 0
Address of an object that is of the right type
Address one past the end of an object
Another pointer of the same type
• Which of these are okay?
int ival;
int zero = 0;
const int c_ival = 0;
int *pi = &ival;
pi = zero;
pi = c_ival;
pi = 0;
• If you’re using the <cstdlib> header, you can also
use NULL
• contains the line
#define NULL 0
…which tells the preprocessor to interpret any place in
the program that has the word NULL as being 0
• If you try to dereference a null pointer, you will
get a Segmentation Fault
• There’s also void pointers, which can hold the
address for any kind of type
double obj = 3.14;
double *pd = &obj;
int myint = 3;
void *pv = &obj;
pv = pd;
pv = &myint;
• We have pointers so we can operate on the
objects they point to
string s(“hello world”);
string *sp = &s;
cout << *sp;
• This dereferences the pointer
• Which of the following are legal definitions?
(a) int *ip;
(b) string s, *sp = 0;
(c) int i; double *dp = &i;
(d) int *ip, ip2;
(e) const int i=0, *p = i;
(f) string *p = NULL;
• Assigning through pointers
string s1(“some value”);
string s2(“another value”);
string *sp1 = &s1;
string *sp2 = &s2;
*sp1 = “a new value”;
cout << s1 << endl << s2 << endl;
sp1 = sp2;
*sp1 = “a new value”;
cout << s1 << endl << s2 << endl;
Pointers (to pointers)
• Since pointers are also objects, we can point
to them too
int ival = 1024;
int *pi = &ival;
int **ppi = &pi;
(Is your head exploding yet?)
• What does this program do?
int i = 42, j = 1024;
int *p1 = &I, *p2 = &j;
*p2 = *p1 * *p2;
*p1 *= *p1;
Pointers and Arrays
• Yesterday we looked at iterator arithmetic
• Pointer arithmetic is much the same
int ia[] = {0,2,4,6,8};
int *ip = ia;
ip = &ia[4];
ip = ia;
int *ip2 = ip+4; // equivalent to &ia[4];
Pointers and Arrays
• We can dereference an expression
int last = *(ia + 4); // refers to the 4th value
// in the array
last = *ia + 4; // adds 4 to the 0th value
// in the array
Pointers and Arrays
• Subscripting an array and subscripting a
pointer are effectively the same operation
int *p = &ia[2];
int j = p[1];
// 1 int in front of where p is
int k = p[-2]; // 2 ints behind where p is
Pointers and Arrays
• Another way to write the for loop we used
const size_t arr_sz = 5;
int int_arr[arr_sz] = {0,1,2,3,4};
for (int *pbegin = int_arr, *pend = int_arr + arr_sz;
pbegin != pend;
cout << *pbegin << ‘ ‘;
• Where is p1 after this line of code?
– p1 and p2 are both int pointers somewhere in the
same array
p1 += p2 – p1;
• We can make pointers to objects for types we create
• There’s a special operator, ->, for accessing public
members of those objects
• Shorthand for (*ptr).
Sales_item item1, item2;
Sales_item *pitem = &item1;
Pointers and const
• Pointers to const objects should not be able to change
• Pointers to const objects must use the const keyword
const double mydbl = 17.0;
const double mydbl2 = 43.0;
double nonconst = 25;
const double *cptr = &mydbl; // ok
cptr = &mydbl2; // also ok: the ptr isn’t const
Pointers and const
double dval = 3.14;
const double *cptr = &dval;
dval = 3.14159;
*cptr = 3.14159;
double *ptr = &dval;
*ptr = 2.72;
cout << *cptr;
Dynamic Arrays
• We said that arrays have fixed size
• Not completely true; it’s possible to create
dynamically-sized arrays
int *pia = new int[10]; // array of 10
// uninitialized ints
int *pia = new int[10] (); // array of 10
// initialized ints
Dynamic Arrays
• Using the new keyword causes the allocation
to occur
• When we’re done with an array created
dynamically, we need to get rid of it with the
delete keyword
• Otherwise it will result in a memory leak
delete [] pia;
• So far the only one we’ve seen is main()
• Functions are defined by a name, and a set of operands
• Function prototype is the return type, name and
int gcd(int v1, int v2) {
while (v2) {
int temp = v2;
v2 = v1 % v2;
v1 = temp;
return v1;
• In order to use a function, we use the call
operator, or ()
// get values from standard input
cout << “Enter two values: \n”;
int i,j;
cin >> i >> j;
// call gcd on arguments i and j
// and print their greatest common divisor
cout << “gcd: “ << gcd(i,j) << endl;
• The parameters (int v1 and int v2 for gcd) of the
function are initialized when the function is called
• The arguments to a function become the parameters
once inside the function
– Sometimes you’ll hear these used interchangeably
• The type of an argument must match its coresponding
• Any parameters and any variables defined inside the
function have local scope
– they exist only inside the body of the function
• Must also have a return type, which says what kind of
object will come out of the function
• Return types can be built-in types, class types, or
compound types
bool is_present(int *, int);
int count(const string &, char);
Date &calendar(const char*);
void process();
• Can’t return a function or an array, but can return
pointers to functions and arrays
• You must specify a return type in Standard C++ (even if
it’s void, or int)
• Parameter lists must specify the type for every
void process() { /* … */ } // legal
void process(void) { /* … */ } // legal
int manip(int v1, v2){ /* … */} // illegal
int manip(int v1, int v2){/* … */} // legal
• Names are optional on parameters, but to use
them, they must have names
• Parameters are checked at compile-time to see if
the argument types match
gcd(“hello”, “world”);
gcd(42, 10, 0);
• All of the above cause compile-time errors
gcd(3.14, 6.29); // no error; converts doubles to ints
• What’s wrong with these function definitions (if
(a) int f() {
string s;
// …
return s;
(b) f2(int i) { /* … */ }
(c) int calc(int v1, int v1) { /* … */ }
(d) double square(double x) return x * x;
Parameters that are not references are initialized by copying the arguments
Changes to the values inside the function will not change them outside the
void swap(int a, int b) {
int c = a;
a = b;
b = c;
int main() {
int x = 5, y = 4;
cout << “The values are: “ << x << “ “ << y << endl;
return 0;
The values are: 5 4
• In order to change the values, we need to use pointers!
void swap(int *a, int *b) {
int c = *a;
*a = *b;
*b = c;
int main() {
int x = 5, y = 4;
cout << “The values are: “ << x << “ “ << y << endl;
return 0;
The values are: 4 5
• Or we can use references:
void swap(int &a, int &b) {
int c = a;
a = b;
b = c;
int main() {
int x = 5, y = 4;
cout << “The values are: “ << x << “ “ << y << endl;
return 0;
The values are: 4 5
• Do not return a reference to a local variable
// Disaster: Function returns a reference to a local object
const string &manip(const string& s) {
string ret = s;
// transform ret in some way
return ret; // Wrong: returning a reference to a local object!
Class Functions
• Back to Sales_item
class Sales_item {
// operations on Sales_item objects
double avg_price() const;
bool same_isbn(const Sales_item &rhs) const { return
isbn == rhs.isbn }
std::string isbn;
unsigned units_sold;
double revenue;
Class Functions
• Looking at same_isbn:
bool same_isbn(const Sales_item &rhs) const
{ return isbn == rhs.isbn }
• Body is a block
• isbn is private, but this is not an error – private
members can be accessed by class functions
Class Functions
• Member functions of a class have an extra
parameter that isn’t in the parameter list
• That parameter is this – a reference to the
object on which the function was called
• It’s almost as if a call gets translated like so:
Sales_item::same_isbn(&total, trans);
• We’ve talked about the default constructor…
…but what’s a constructor?
• A constructor is a member function that gets run when
an object of the type is initialized
– In turn, constructors should make sure that every data
member is initialized
• Constructors have the same name as the class
• Sales_item needs to define the default constructor,
which takes no arguments
• Constructors must be public, or it is not possible to
create objects of that type!
class Sales_item {
// operations on Sales_item objects
double avg_price() const;
bool same_isbn(const Sales_item &rhs) const
return isbn == rhs.isbn }
// default constructor needed to initialize basic-type members
Sales_item(): units_sold(0), revenue(0.0) {}
std::string isbn;
unsigned units_sold;
double revenue;
• The colon and what follows is the constructor
initialization list
• List of member names, followed by the initial
value in parentheses
• We don’t need to specify an initial value for isbn,
since std::string has its own default constructor
(makes it the empty string)
• If we don’t explicitly define a default constructor,
a synthesized default constructor will be
generated for us
– Sets all basic types to 0; rest use default constructor
Overloaded Functions
• You can rewrite functions so that they behave
differently when used with a different
parameter list
• Sales_item will use this to redefine the basic
Sales_item class
• We had enough yesterday to start to write the
• Now we can finish writing the code!
Sales_item class
• Operations:
– Use addition operator + to add two Sales_items
– Use input operator << to read a Sales_item
– Use output operator >> to write a Sales_item
– Use assignment operator = to assign one
Sales_item to another
– Call same_isbn function to see if two Sales_items
are the same book
Administrivia Updates
• Exam on Tuesday
– First half of the class will be review / question
session; second half will be the midterm
– Covering anything we did from this week
• Project #2 due date will be moved to
Wednesday 10pm
– Will be released tomorrow
• HW #2 still due beginning of class Tuesday