You are on page 1of 49

ECE 150, Section 001, Fall 2017

Leftovers

1 / 44
Selection: Ternery and Switch
?:
switch
Implementation

Looping: for(;;) and do { } while()


for( ; ; )
do { } while ()

Arrays: Strings and Multi-Dimensional Arrays


Strings
Multi-Dimensional Arrays
Pointers

2 / 44
Selection: Ternery and Switch

Looping: for(;;) and do { } while()

Arrays: Strings and Multi-Dimensional Arrays

3 / 44
Selection: Ternery and Switch
?:
switch
Implementation

Looping: for(;;) and do { } while()


for( ; ; )
do { } while ()

Arrays: Strings and Multi-Dimensional Arrays


Strings
Multi-Dimensional Arrays
Pointers

4 / 44
Ternary Selection

I (condition) ? (if true) : (if false)

Option 1: if {} else {} Option 2: ?:

if (a > b) max = a > b ? a : b;


max = a;
else
max = b;

I Useful for conditional initialization among other things


I Widely used, though can be difficult to read in code
I Readability can be helped with judicious parentheses

5 / 44
Selection: Ternery and Switch
?:
switch
Implementation

Looping: for(;;) and do { } while()


for( ; ; )
do { } while ()

Arrays: Strings and Multi-Dimensional Arrays


Strings
Multi-Dimensional Arrays
Pointers

6 / 44
else if else if else if . . .

I What if we want x == A or x == B or x == C?

if (x == A) {
...
} else if (x == B) {
...
} else if (x == C) {
...
}

I What about: x == A or x == B or ... or x == Y or x == Z?


I What’s wrong with if else if else if else if ...?

7 / 44
switch () { . . . }
switch (expression) {
case A:
...
break;
case B:
...
break;
...
case Z:
...
break;
default:
...
break;
}
I Question: Why “break” after “default”?

8 / 44
switch () { . . . }
switch (expression) {

Selection: Ternery and Switch case A:

2017-10-26
...
break;
case B:

switch ...
break;
...

switch () { . . . } case Z:
...
break;
default:
...
break;
}
I Question: Why “break” after “default”?

1. Can only switch on an expression that evaluates to integer type.


2. i.e., char, short, long, and long long, signed or unsigned.
3. (It can actually include enumerated types, and even class types, but we
haven’t discussed that, so it can be ignored for now.)
4. The case must have a constant expression, of integer type, not a
computed expression (this is for constant-time implementation).
5. “break;” is NOT required but is generally required for code correctness.
6. if “break” is not present, the code continues executing in the next case
7. “break” is an unconditional jump to the end of the switch statement
8. “default:” is NOT required but is extremely good practice
9. if “default” is not present, and there is no match with any case execution
will continue after the “switch” statement. This is undesirable because it
likely represents something unexpected and the code is silent on the
event. If nothing else, the fact that this has happened should be noted.
10. Spacing is required more than is shown here.
11. Order of cases, and default, is only important if one or more “break”
statements are missing.
A is for APL
char inputChar;
cout << "Enter a letter: ";
cin >> inputChar;

switch (inputChar) {
case ’a’:
case ’A’:
cout << "A is for APL and Ada and ALGOL" << endl;
break;

...

default:
cout << "Try entering a letter next time ..." << endl;
break;
}
9 / 44
Selection: Ternery and Switch
?:
switch
Implementation

Looping: for(;;) and do { } while()


for( ; ; )
do { } while ()

Arrays: Strings and Multi-Dimensional Arrays


Strings
Multi-Dimensional Arrays
Pointers

10 / 44
Inefficient Implementation
I Convert to “if else if else if ... ”

Option 1: switch() Option 2: if {} else if {} else . . .

switch(X) { if ((X) == A) {
case A: code for A;
code for A; }
break; else if ((X) == B) {
case B: code for B;
code for B; }
break; ...
... else {
default: code for default;
code for default; }
break;
}
I Question: Why is this inefficient?
11 / 44
Inefficient Implementation
I Convert to “if else if else if ... ”

Selection: Ternery and Switch


2017-10-26
Option 1: switch() Option 2: if {} else if {} else . . .

switch(X) { if ((X) == A) {

switch case A:
code for A;
break;
}
code for A;

else if ((X) == B) {
case B: code for B;
Inefficient Implementation code for B;
break;
...
}
...
else {
default: code for default;
code for default; }
break;
}
I Question: Why is this inefficient?

• This transformation assumes a simple one-to-one mapping with


each case statement having a single choice, not paired as in
Leftovers-switch.cpp, which required a double test for each set of
code.
• More complex situations arise if there is a case statement with some
code but then no break before the next case statement.
• It is an inefficient implementation because the further down the
case statement is within the switch, the longer it takes for it to be
executed.
• Consider Leftovers-switch.cpp: there are 26 options, each paired; if
“z” is selected, there are 50 prior comparisons that must be
performed before reaching the ’z’ option.
• This method is used by the C/C++ compiler when there are few
(2–3) cases and they are straightforward.
Efficient Implementation 1: Dense Target
switch(x) {
case 0:
code for 0;
break;
case 1:
code for 1;
break;
case 2:
...
case 63:
code for 63;
break;
default:
code for default;
break;
}
I Target case constants are 0–63 and a default
12 / 44
Jump Tables
0 Address of Code for Case 0
1 Address of Code for Case 1
2 Address of Code for Case 2
..
.
62 Address of Code for Case 62
63 Address of Code for Case 63

PseudoCode for Implementation:

if ((x < 0) or (x > 63))


destinationAddress = addressOfDefaultCode
else
destinationAddress = jumpTable[x]
goto destinationAddress

13 / 44
Assembly Code for Dense Target Example
...
Instruction Space 602 Code for Case 62
Address Code ...
... 616 JMP 627
429 LD 902, R1 617 Code for Case 63
430 JLZ R1,623 ...
431 SUBi R1,63,R2 622 JMP 627
432 JGZ R2,623 623 Code for default case
433 LDi 913, R2 ...
434 ADD R2,R1,R2 626 JMP 627
435 LD R2,R3 627 Code after switch
436 JMP R3 ...
437 Code for Case 0 Data Space
... Address Data
463 JMP 627
...
464 Code for Case 1 902 -
... ...
479 JMP 627 913 437
480 Code for Case 2 914 464
... 915 480
496 JMP 627 ...
... 975 602
976 617
14 / 44
Assembly Code for Dense Target Example
...
Instruction Space 602 Code for Case 62

Selection: Ternery and Switch Address Code ...

2017-10-26
... 616 JMP 627
429 LD 902, R1 617 Code for Case 63
430 JLZ R1,623 ...
431 SUBi R1,63,R2 622 JMP 627

switch 432 JGZ R2,623


433 LDi 913, R2
434 ADD R2,R1,R2
435 LD R2,R3
623

626
627
Code for default case
...
JMP 627
Code after switch

Assembly Code for Dense Target Example


436 JMP R3 ...
437 Code for Case 0 Data Space
... Address Data
463 JMP 627
...
464 Code for Case 1 902 -
... ...
479 JMP 627 913 437
480 Code for Case 2 914 464
... 915 480
496 JMP 627 ...
... 975 602
976 617

• LDi <value>, Ri: Load immediate: rather than treating the “value” as a
memory address, simply treat it as a value to be loaded into Ri.
• LDi <value>, Ri: Ri ← <value>
• LD Ri, Rj: Load indirect: rather than load from a specified memory
address, consider the contents of a register as a memory address and load
from that location
• LD Ri,Rj: Rj ← data at memory location contained in Ri
• JMPi: Jump indirect: rather than jump to a specified memory address,
consider the contents of a register as memory address and jump to that
location
• JMPi Ri: PC ← data in Ri
• Memory location of “x” is 902
• Memory location of jump table is 902–976

• This pseudo-assembly code is to give you a sense of what’s going on
• You are not expected to understand it in depth at this stage of the course.
Efficient Implementation 2: Sparse Target

I What if there are some gaps in the


target range?
I If only a few, use the default
address
I e.g., consider if “case 36:” was not
present in the previous example
I jumpTable[36] = 623

I What if the target range is sparse?


I e.g., we have several cases, including 12, 24553, and 1622975
I Too many cases for an if/else transformation
I Too sparse for a jump table

15 / 44
Hash Functions

If there are N possible inputs over a


domain D, a perfect hash function
will map each distinct input to a dis-
tinct value in the range R between 0
and N − 1.

In the sparse case, the compiler will hash the switch expression to
a significally smaller range, which will then be looked up with a
jump table.

(The modulo (%) operator can act as an imperfect hash function,


and used as the basis for creating a useful hash function.)

16 / 44
Hash Functions

Selection: Ternery and Switch


2017-10-26
If there are N possible inputs over a
domain D, a perfect hash function

switch will map each distinct input to a dis-


tinct value in the range R between 0
and N − 1.

Hash Functions In the sparse case, the compiler will hash the switch expression to
a significally smaller range, which will then be looked up with a
jump table.

(The modulo (%) operator can act as an imperfect hash function,


and used as the basis for creating a useful hash function.)

• Hash(x) maps to the jump table, but the jump table must include
not only the jump address, but what value of “x” for which it
contains the address, since there will be many input “x” values that
are not valid cases.
• Hash collisions must also be accounted for.
• The concept of hashing will come up again somewhat in this course,
a lot in ECE 250, Algorithms and Data Structures, and is very
widely used in programming.
• Hashing is also used in other ECE areas. For example, various
security protocols use special cryptographic hash functions.
Selection: Ternery and Switch
?:
switch
Implementation

Looping: for(;;) and do { } while()


for( ; ; )
do { } while ()

Arrays: Strings and Multi-Dimensional Arrays


Strings
Multi-Dimensional Arrays
Pointers

17 / 44
Selection: Ternery and Switch
?:
switch
Implementation

Looping: for(;;) and do { } while()


for( ; ; )
do { } while ()

Arrays: Strings and Multi-Dimensional Arrays


Strings
Multi-Dimensional Arrays
Pointers

18 / 44
for( ; ; ): Formalization of common usage pattern
Let’s take a closer look at a common use of while():
initialize some variables
while (test condition) {
do stuff;
do more stuff;
...
change values that might affect the test condition;
}
for(;;) is a syntactic variant of while() that formalizes
I initialization
I change

for(initialize ; test expression ; change) {


do stuff;
do more stuff;
...
}
19 / 44
for(;;) example
N
X
i2
i=0

int sum = 0; int sum = 0


int i = 0;
int n; int n;
cin >> n; cin >> n;
while( i < n ) { for (int i = 0; i <= n; ++i) {
sum += i*i; sum += i*i;
++i; }
}
I What is the scope of “i”?

20 / 44
Translating for(;;) to while()

A;
for(A;B;C) { while(B) {
body body
of of
loop loop
} C;
}

21 / 44
for( ; ; ) — Important Nuance I

I The following two pieces of code are not exactly alike.

int i = 0; for(int i = 0; i < n; i++) {


while(i < n) { ...
... }

i++;
}

I On the right, scope of i is the loop only.


I Attempt to access i outside the loop gives compiler error.
I This is a good thing.

22 / 44
for( ; ; ) — Important Nuance II

I General syntax of for is:

for(statement1; expression; statement2) {


...
}

I statement1, statement2, and expression can be any legal


C++ statements/expression.
I Good programming practice:
I use statement1 for loop-initialization only.
I statement2 for loop-index change only.
I Ensure no side-effects in statements or expression.

23 / 44
Selection: Ternery and Switch
?:
switch
Implementation

Looping: for(;;) and do { } while()


for( ; ; )
do { } while ()

Arrays: Strings and Multi-Dimensional Arrays


Strings
Multi-Dimensional Arrays
Pointers

24 / 44
do { } while()

I Why test at the beginning of the loop?


I Why not test at the end of the loop?
I Implication: the loop will execute at least once!

do {
body
of
loop
} while (condition);

Example: Leftovers-keyboard.cpp

25 / 44
Translate “do { } while()” to “while()”

int firstTime = 1;
do { while (firstTime || condition) {
body body
of of
loop loop
} while (condition); firstTime = 0;
}

(Depending on the specific test condition, this kind of formality is


often unnecessary)

26 / 44
What if I want to exit in the middle of a loop?

I break isn’t just for switch!


I It will exit any loop
I Try not to use it!

for (int i = 0; i < WORST_CASE; ++i) {


if (f(i) == valueIAmLookingFor) {
break;
}
}

DON’T DO THIS!

27 / 44
Selection: Ternery and Switch

Looping: for(;;) and do { } while()

Arrays: Strings and Multi-Dimensional Arrays

28 / 44
Selection: Ternery and Switch
?:
switch
Implementation

Looping: for(;;) and do { } while()


for( ; ; )
do { } while ()

Arrays: Strings and Multi-Dimensional Arrays


Strings
Multi-Dimensional Arrays
Pointers

29 / 44
String: Array of char
I String Literal is a null-terminated array of char.

const int maxLength = 100;


char s1[myMaxStringLength];
char s2[] = "Huawei";

s1[0] = ’a’;
s1[1] = ’b’;
s1[2] = ’c’;
s1[3] = (char)0;

cout << "s1: " << s1 << "\ns2: " << s2 << endl;

See Leftovers-simplestring.cpp

30 / 44
Selection: Ternery and Switch
?:
switch
Implementation

Looping: for(;;) and do { } while()


for( ; ; )
do { } while ()

Arrays: Strings and Multi-Dimensional Arrays


Strings
Multi-Dimensional Arrays
Pointers

31 / 44
Multi-Dimensional Arrays

I Arrays may be more than 1-dimensional.

See Leftovers-twodimensional.cpp

32 / 44
Command-line Input

I Suppose we declare int myArr[20];


I Recall: myArr IS an address.
I Specifically, the base address of the array.
I So, a 2-dimensional array int my2DArr[10][20];
I Can be thought of as 10 1-dimensional arrays.
I Each of 20 int.
I But what would be the base address of those 10 arrays?
I Do we need to keep 10 base addresses?

See Leftovers-commandlinearguments.cpp

33 / 44
Selection: Ternery and Switch
?:
switch
Implementation

Looping: for(;;) and do { } while()


for( ; ; )
do { } while ()

Arrays: Strings and Multi-Dimensional Arrays


Strings
Multi-Dimensional Arrays
Pointers

34 / 44
Array as an Address
Recall, from Leftovers-arrayOps.cpp:
int a[5];
...
cout << "Element 0: " << a[0];
cout << "Element 0: " << *a;
...
cout << "Element 3: " << a[3];
cout << "Element 3: " << *(a+3);

I Question: what is a?
I Is it a variable?
I Does it contain a value?
I If so, is that value changable?
I What kind of value?
I Answer: an address or a reference.
I A value that identifies a location in memory.

35 / 44
Syntatic Details

I *x, where x contains an address, dereferences x.


I &x, where x is a variables, yields the address of x.
I int *x; declares x to be of type “address of an int.”

See Leftovers-addressGames.cpp

36 / 44
Explanation via Memory-Layout

Suppose x is allocated at the


memory location 3FEA 13C2.
What is the output produced
by each of Lines 2, 3, 4, 5?

1: unsigned x;
...
2: cout << x;
3: cout << &x;
4: cout << *x;
5: cout << **x;

37 / 44
Explanation via Memory-Layout

Arrays: Strings and Multi-Dimensional Arrays


2017-10-26 Suppose x is allocated at the
memory location 3FEA 13C2.

Pointers What is the output produced


by each of Lines 2, 3, 4, 5?

1: unsigned x;

Explanation via Memory-Layout ...


2:
3:
cout << x;
cout << &x;
4: cout << *x;
5: cout << **x;

• 2: the value stored in x, which is 35260931 in decimal.


• 3: the location at which x is allocated, which is 3FEA 13C2 in hex.
• 4: the value stored at the address, which is the value of x. I.e., 42
in decimal.
• 5: whatever is currently in the memory location 0000 002A in hex.
NOTE: not all memory locations are legally accessible by a program.
So one may get a runtime error: “segmentation fault.”
So what is a “Pointer?”

I C/C++ jargon for an address/reference


I E.g., if we declare: int *x;
I Then x is a “pointer” to something of type int.

38 / 44
Relationship to Arrays

I Suppose I declare int a[10];


I Then I can interpret a as a const pointer to int
I (a is not a pointer, in that it consumes no memory, it contains
no value; it IS a value)
I I can dereference the pointer, *a = ...
I I can dereference the pointer, *(a+3) = ...
I Which accesses the contents of the array

39 / 44
Dynamic Memory-Allocation I
I Two ways of allocation memory “dynamically.”
I Way 1:

int size = 10;


char a[size];

I Used to be illegal.
I Supported by more recent versions of compiler.
I Does not allow initialization.

int size = 10;


char a[size] = { ... };

Won’t work

40 / 44
Dynamic Memory-Allocation II

I Way 2:

in size = 10;
char *a; // A pointer to char
a = new char[size];

I Two keywords: new, delete.


I new allocates. delete deallocates.
I Warning: new[] != new. delete != delete[].

41 / 44
Example – Allocated vs. not

unsigned *a;
cout << a[5]; // ILLEGAL access!
// May crash program

a = new unsigned[10];
cout << a[5]; // legal access

delete[] a; // Deallocate. Access to


// a[5] no longer legal

See Leftovers-dynamicmemoryallocation.cpp

42 / 44
Command-Line Arguments, Revisited

I Recall, how we get access to command-line arguments:

int main(int argc, char *argv[])

I So argv is an array of pointers to characters.


I But a pointer can be seen as the base-address of an array.
I In this case, those arrays are null-terminated.

43 / 44
Image Credits. . .

To come. . .

44 / 44

You might also like