CS31 Discussion 1E
Spring 17’: week 08
TA: Bo-Jhang Ho
bojhang@cs.ucla.edu
Credit to former TA Chelsea Ju
CS31 Discussion 1E Spring 17: week 08 TA: Bo-Jhang Ho - - PowerPoint PPT Presentation
CS31 Discussion 1E Spring 17: week 08 TA: Bo-Jhang Ho bojhang@cs.ucla.edu Credit to former TA Chelsea Ju Project 5 - Map cipher to crib } Approach 1: For each pair of positions, check two letters in cipher and crib are both identical or
TA: Bo-Jhang Ho
bojhang@cs.ucla.edu
Credit to former TA Chelsea Ju
} Approach 1: For each pair of positions, check two letters
} For each pair of positions pos1 and pos2,
cipher[pos1] == cipher[pos2] should equal to crib[pos1] == crib[pos2] } Approach 2: Get the positions of the letter and compare
} For each position pos,
indexes1 = getAllPositions(cipher, pos, cipher[pos]) indexes2 = getAllPositions(crib, pos, crib[pos]) indexes1 should equal to indexes2
} Approach 3: Generate the mapping
} We first have a mapping array char cipher2crib[128] = {‘\0’}; } Then whenever we attempt to map letterA in cipher to letterB
} Is cipher2crib[letterA] != ‘\0’?
// implies letterA has been used
} Then cipher2crib[letterA] should equal to letterB
} If no violation happens,
} cipher2crib[letterA] = letterB;
} int *a;
} a is a variable, whose data type is int * } a stores an address of some integer
} & - address-of operator
} &b means I want to get the memory address of variable b
} * - dereference operator
} *c means I want to retrieve the value in address c
Address Memory Contents
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
1 byte
Address Memory Contents
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
1 byte = 8 bits
A bit has 2 states
Address Memory Contents
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
1 byte = 8 bits
A bit has 2 states A byte has 256 (=28) states
Address Memory Contents
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
1 byte = 8 bits
A bit has 2 states A byte has 256 (=28) states A char takes 1 byte An int takes 4 bytes A double takes 8 bytes
} Basic data types
Type Bytes Bits Value range char 1 8
short 2 16
int 4 32
long long 8 64
float 4 32
double 8 64
Address Memory Contents
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
1 byte = 8 bits
A bit has 2 states A byte has 256 (=28) states My has 16GB ram
Address Memory Contents
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
1 byte = 8 bits
A bit has 2 states A byte has 256 (=28) states My has 16GB ram = 16,000,000,000 bytes!
int main() { int a = 5; int b = 3; double c = 3.5; int d = b - a; return 0; } Address Memory Contents
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
int main() { int a = 5; int b = 3; double c = 3.5; int d = b - a; return 0; } Address Memory Contents
1000
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
a
int main() { int a = 5; int b = 3; double c = 3.5; int d = b - a; return 0; } Address Memory Contents
1000
1004
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
a b
int main() { int a = 5; int b = 3; double c = 3.5; int d = b - a; return 0; } Address Memory Contents
1000
1004
1008
1016 1017 1018 1019
a b c
int main() { int a = 5; int b = 3; double c = 3.5; int d = b - a; return 0; } Address Memory Contents
1000
1004
1008
1016
a b c d
int main() { int a = 5; int *b = &a; a++; *b += 2; cout << a << endl; cout << *b << endl; return 0; } Address Memory Contents
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
int main() { int a = 5; int *b = &a; a++; *b += 2; cout << a << endl; cout << *b << endl; return 0; } Address Memory Contents
1000
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
a
int main() { int a = 5; int *b = &a; a++; *b += 2; cout << a << endl; cout << *b << endl; return 0; } Address Memory Contents
1000
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
a
int main() { int a = 5; int *b; // declare a pointer var b = &a; // set address a++; *b += 2; cout << a << endl; cout << *b << endl; return 0; } Address Memory Contents
1000
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
a
int main() { int a = 5; int *b; // declare a pointer var b = &a; // set address a++; *b += 2; cout << a << endl; cout << *b << endl; return 0; } Address Memory Contents
1000
1004
(address)
1012 1013 1014 1015 1016 1017 1018 1019
a b
int main() { int a = 5; int *b; // declare a pointer var b = &a; // set address a++; *b += 2; cout << a << endl; cout << *b << endl; return 0; } Address Memory Contents
1000
1004
(address)
1012 1013 1014 1015 1016 1017 1018 1019
a b
int main() { int a = 5; int *b; // declare a pointer var b = &a; // set address a++; *b += 2; cout << a << endl; cout << *b << endl; return 0; } Address Memory Contents
1000
1004
(address)
1012 1013 1014 1015 1016 1017 1018 1019
a b
5 à
int main() { int a = 5; int *b; // declare a pointer var b = &a; // set address a++; *b += 2; cout << a << endl; cout << *b << endl; return 0; } Address Memory Contents
1000
1004
(address)
1012 1013 1014 1015 1016 1017 1018 1019
a b
6 à
int main() { int a = 5; int *b; // declare a pointer var b = &a; // set address a++; *b += 2; cout << a << endl; cout << *b << endl; return 0; } Address Memory Contents
1000
1004
(address)
1012 1013 1014 1015 1016 1017 1018 1019
a b
} Output
} 8
int main() { int a = 5; int *b; // declare a pointer var b = &a; // set address a++; *b += 2; cout << a << endl; cout << *b << endl; return 0; } Address Memory Contents
1000
1004
(address)
1012 1013 1014 1015 1016 1017 1018 1019
a b
} Output
} 8
int main() { int a; int b = a + 3; int *c; *c = 27; return 0; } Address Memory Contents
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
int main() { int a; int b = a + 3; int *c; *c = 27; return 0; } Address Memory Contents
1000
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
a
int main() { int a; int b = a + 3; int *c; *c = 27; return 0; } Address Memory Contents
1000
1004
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
a b
int main() { int a; int b = a + 3; int *c; *c = 27; return 0; } Address Memory Contents
1000
1004
1008
(address)
1016 1017 1018 1019
a b c
int main() { int a; int b = a + 3; int *c; *c = 27; return 0; } Address Memory Contents
1000
1004
1008
(address)
1016 1017 1018 1019
a b c
} May manipulate a piece of
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c = &b[1]; *c = 100; return 0; } Address Memory Contents
1000 1002 1004 1006 1008 1010 1012 1014 1016 1018 1020 1022 1024 1026 1028 1030 1032 1034 1036 1038
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c = &b[1]; *c = 100; return 0; } Address Memory Contents
1000
3
1004 1006 1008 1010 1012 1014 1016 1018 1020 1022 1024 1026 1028 1030 1032 1034 1036 1038
a
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c = &b[1]; *c = 100; return 0; } Address Memory Contents
1000
3
1004
10
1008
20
1012
30
1016
40
1020
50
1024 1026 1028 1030 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c = &b[1]; *c = 100; return 0; } Address Memory Contents
1000
3
1004
10
1008
20
1012
30
1016
40
1020
50
1024 1026 1028 1030 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c; // declare a pointer c = &b[1]; // get address *c = 100; return 0; } Address Memory Contents
1000
3
1004
10
1008
20
1012
30
1016
40
1020
50
1024 1026 1028 1030 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c; // declare a pointer c = &b[1]; // get address *c = 100; return 0; } Address Memory Contents
1000
3
1004
10
1008
20
1012
30
1016
40
1020
50
1024
??
(address) 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b c
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c; // declare a pointer c = &b[1]; // get address *c = 100; return 0; } Address Memory Contents
1000
3
1004
10
1008
20
1012
30
1016
40
1020
50
1024
1008
(address) 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b c
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c; // declare a pointer c = &b[1]; // get address *c = 100; return 0; } Address Memory Contents
1000
3
1004
10
1008
100
1012
30
1016
40
1020
50
1024
1008
(address) 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b c
20 à
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c = &b[1]; c = c + 1; // Or, // c += 1; // c++; *c = 27; return 0; } Address Memory Contents
1000
3
1004
10
1008
20
1012
30
1016
40
1020
50
1024
1008
(address) 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b c
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c = &b[1]; c = c + 1; // Or, // c += 1; // c++; *c = 27; return 0; } Address Memory Contents
1000
3
1004
10
1008
20
1012
30
1016
40
1020
50
1024
1012
(address) 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b c
1008à
} What? 1008 + 1 = 1012?
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c = &b[1]; c = c + 1; // Or, // c += 1; // c++; *c = 27; return 0; } Address Memory Contents
1000
3
1004
10
1008
20
1012
30
1016
40
1020
50
1024
1012
(address) 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b c
1008 à
} Pointer arithmetic
} We should interpret as adding
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c = b; c += 2; *c = 27; return 0; } Address Memory Contents
1000
3
1004
10
1008
20
1012
30
1016
40
1020
50
1024
1004
(address) 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b c
} Array name is a pointer
} b can be treated as an int*
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c = b; c += 2; *c = 27; return 0; } Address Memory Contents
1000
3
1004
10
1008
20
1012
30
1016
40
1020
50
1024
1012
(address) 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b c
} Array name is a pointer
} b can be treated as an int*
1004à
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c = b; c += 2; *c = 27; return 0; } Address Memory Contents
1000
3
1004
10
1008
20
1012
27
1016
40
1020
50
1024
1012
(address) 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b c
} Array name is a pointer
} b can be treated as an int*
30 à
} Array name can be considered as start point
} Technically, it’s the base address
} The index can be considered as the offset
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c = b; cout << b[-1] << endl; return 0; } Address Memory Contents
1000
3
1004
10
1008
20
1012
27
1016
40
1020
50
1024
1004
(address) 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b c
b[0] b[-1] b[1] b[2] b[3] b[4] b[5] b[6] b[7] b[8] b[9] b[-2] b[-3] b[-4]
} If x is a pointer, you can treat x as an array
} Meaning, you can have something like x[3]
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c = b + 2; cout << c[-2] << endl; return 0; } Address Memory Contents
1000
3
1004
10
1008
20
1012
27
1016
40
1020
50
1024
1012
(address) 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b c
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c = b + 2; cout << c[-2] << endl; return 0; } Address Memory Contents
1000
3
1004
10
1008
20
1012
27
1016
40
1020
50
1024
1012
(address) 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b c
c[0]
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c = b + 2; cout << c[-2] << endl; return 0; } Address Memory Contents
1000
3
1004
10
1008
20
1012
27
1016
40
1020
50
1024
1012
(address) 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b c
c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7]
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c = b + 2; cout << c[-2] << endl; return 0; } Address Memory Contents
1000
3
1004
10
1008
20
1012
27
1016
40
1020
50
1024
1012
(address) 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b c
c[-2] c[-3] c[-1] c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] c[-4] c[-6]
int main() { int a = 3; int b[5] = {10, 20, 30, 40, 50}; int *c = b + 2; cout << c[-2] << endl; return 0; } Address Memory Contents
1000
3
1004
10
1008
20
1012
27
1016
40
1020
50
1024
1012
(address) 1032 1034 1036 1038
a
b[0] b[1] b[2] b[3] b[4]
b c
c[-2] c[-3] c[-1] c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] c[-4] c[-6]
} It is actually a valid
} Array name is a pointer } Pointer arithmetic
} Let’s say x is a pointer, n is an integer } x + n is a pointer (memory address) after n elements of x } x - n is a pointer before n elements of x
} Treat a pointer as an array
} Again, let’s say x is a pointer, n is an integer } x[n] means to access nth element counted from x } x[n] equivalent to *(x + n)
} From caller: } In the function:
} Approach 3: Generate the mapping
} We first have a mapping array char cipher2crib[128] = {‘\0’};
} Whenever we attempt to map letterA in cipher to letterB in crib: } cipher2crib[letterA] = letterB;
} But there are only 26 letters. Why do we want to have an array
} We use ascii as a key (a.k.a index) in the array } We don’t need to worry about the offset (i.e., x - ‘a’)
} Is there any solution to reduce the size to 26?
bool check(const char cipher[], const char crib[]) { // assume cipher and crib have the same length char cipher2crib[128] = {'\0'}; for (int i = 0; cipher[i] != '\0'; i++) { char letterA = cipher[i]; char letterB = crib[i]; if (cipher2crib[letterA] != '\0’ && cipher2crib[letterA] != letterB) return false; cipher2crib[letterA] = letter } return true; }
bool check(const char cipher[], const char crib[]) { // assume cipher and crib have the same length char trueArray[26] = {'\0'}; char *cipher2crib = trueArray – 'a'; for (int i = 0; cipher[i] != '\0'; i++) { char letterA = cipher[i]; char letterB = crib[i]; if (cipher2crib[letterA] != '\0’ && cipher2crib[letterA] != letterB) return false; cipher2crib[letterA] = letter } return true; }
26 elements trueArray cipher2crib
97 elements
Memory footprint
} If we declare int *ptr; and int val;, the
} ptr = &val; } *ptr = val;
} What is &ptr?
} If we declare int *ptr; and int val;, the
} ptr = &val; } *ptr = val;
} What is &ptr?
} &ptr means the address of ptr } The type of &ptr is int** (we call it double pointer) } For example, int** strongPtr = &ptr;
int main() { int a = 3; int *b = &a; int c[5] = {10, 20, 30, 40, 50}; int** d = &b; return 0; } Address Memory Contents
1000
3
1004 1006 1008 1010 1012 1014 1016 1018 1020 1022 1024 1026 1028 1030 1032 1034 1036 1038
a
int main() { int a = 3; int *b = &a; int c[5] = {10, 20, 30, 40, 50}; int** d = &b; return 0; } Address Memory Contents
1000
3
1004
1000
(address) 1012 1014 1016 1018 1020 1022 1024 1026 1028 1030 1032 1034 1036 1038
a b
int main() { int a = 3; int *b = &a; int c[5] = {10, 20, 30, 40, 50}; int** d = &b; return 0; } Address Memory Contents
1000
3
1004
1000
(address) 1012
10
1016
20
1020
30
1024
40
1028
50
1032 1034 1036 1038
a b
c[0] c[1] c[2] c[3] c[4]
c
int main() { int a = 3; int *b = &a; int c[5] = {10, 20, 30, 40, 50}; int** d = &b; return 0; } Address Memory Contents
1000
3
1004
1000
(address) 1012
10
1016
20
1020
30
1024
40
1028
50
1032
1004
(address)
a b
c[0] c[1] c[2] c[3] c[4]
c d
} If we declare int *ptr; and int val;, the
} ptr = &val; } *ptr = val;
} What is *val?
} If we declare int *ptr; and int val;, the
} ptr = &val; } *ptr = val;
} What is *val?
} It won’t compile } * operator (dereference) implies that what it stores is a
} Only pointer variables store memory address
int val; int* ptr1; int** ptr2; int*** ptr3; int**** ptr4; int***** ptr5;
ptr1 = &val; ptr2 = &ptr1; ptr3 = &ptr2; ptr4 = &ptr3; ptr5 = &ptr4; val = *ptr1; ptr1 = *ptr2; ptr2 = *ptr3; ptr3 = *ptr4; ptr4 = *ptr5;
} If we declare int *ptr;, can we hardcode an address
} For example, ptr = 1234;
} No, it won’t compile
} For security issue } It doesn’t make sense that we can get an address beforehand } The same variable can reside in different parts of memory in
int main() { int a; cout << &a << endl; return 0; }
} If we declare int *ptr; and double val;, is the
} ptr = &val;
} If we declare int *ptr; and double val;, is the
} ptr = &val;
} No, it won’t compile
} Pointers are type-aware } We can cast the type: ptr = (int*) &val; } However, that means we use the way we interpret integer to
int main() { double a = 3.5; int *b = (int*) &a; cout << *b << endl; return 0; } Address Memory Contents
1000
3.5
1008
1000
(address) 1016 1018 1020 1022 1024 1026 1028 1030 1032 1034 1036 1038
a b
int main() { double a = 3.5; int *b = (int*) &a; cout << *b << endl; return 0; } Address Memory Contents
1000
3.5
1008
1000
(address) 1016 1018 1020 1022 1024 1026 1028 1030 1032 1034 1036 1038
a b
} If we declare int *ptrI; and double *ptrD;,
} ptrI = ptrD;
} No, it won’t compile
} Pointer type doesn’t match } Though both store memory addresses, how they interpret the
} We can cast the type: ptrI = (int*) ptrD;
double* findFirstNegativePtr(double a[], int n) { for (double* p = a; p < a + n; p++) { if (*p < 0) return p; } return nullptr; }
} Return a pointer
int findFirstNegativeIdx(double a[], int n) { for (int i = 0; i < n; i++) { if (a[i] < 0) return i; } return -1; }
} Return an index
} Problem 1b probably is the most tricky question.
} Define a data structure } A data structure groups different “variables” together } For example, when we describe a 2d coordinate, naturally
} We can also say we declare a new data type
// create a new data type class Point { public: double x; double y; }; // how we use it int main() { Point p; p.x = 1.1; p.y = 2.2; Point r = p; r.y = 3.3; cout << "Point 1: " << p.x << " " << p.y << endl; cout << "Point 2: " << r.x << " " << r.y << endl; return 0; }