Week 01 Tutorial Answers
Getting Started and Recap!
-
Introductions
- Introduce yourself to the other people in the tutorial class
- Discuss what you learned from COMP1511
- Discuss what parts you're still unsure of
Answer:
No answers are available for this question.
New C Syntax
-
Convert the following while loop to a for loop:
int i = 10; while (i >= 0) { printf("%d\n", i); i--; }
Answer:
for (int i = 10; i >= 0; i--) { printf("%d\n", i); }
-
What
while
loop is equivalent to the followingfor
statement:int ch; for (ch = getchar(); ch != EOF; ch = getchar()) { putchar(ch); }
Answer:
Two possibilities:
// the literal translation int ch = getchar(); while (ch != EOF) { putchar(ch); ch = getchar(); } // the common idiom int ch; while ((ch = getchar()) != EOF) { putchar(ch); }
-
Consider a function to check whether the elements in an array occur in ascending order. Duplicates are allowed in the array, as long as they are adjacent.
// Input: // - a is a valid pointer to the start of an array of ints // - n is a positive int indicating how many elements in a[] // Output: // - returns true if a[i] <= a[i + 1] for all i in [0..n - 2] bool isSorted(int *a, int n) { ... }
Implement this function in two styles:
- using COMP1511 C Style
- using for, break/return to exit the loop early
Answer:
COMP1511 style version:
bool isSorted(int *a, int n) { int sorted = true; // assume ok int i = 0; while (i < n - 1 && sorted) { if (a[i] > a[i + 1]) { sorted = false; } i++; } return sorted; }
For-loop C version
bool isSorted(int *a, int n) { int sorted = true; // assume ok int i; for (i = 0; i < n - 1 && sorted; i++) { if (a[i] > a[i + 1]) { sorted = false; } } return sorted; }
Real-world C version:
bool isSorted(int *a, int n) { int i; for (i = 0; i < n - 1; i++) { if (a[i] > a[i + 1]) { return false; } } return true; }
-
Describe the difference in behaviour (and in the final result) of the following two
switch
statements:// S1 switch (ch) { case 'a': printf("eh? "); break; case 'e': printf("eee "); break; case 'i': printf("eye "); break; case 'o': printf("ohh "); break; case 'u': printf("you "); break; } printf("\n");
// S2 switch (ch) { case 'a': printf("eh? "); case 'e': printf("eee "); case 'i': printf("eye "); case 'o': printf("ohh "); case 'u': printf("you "); } printf("\n");
What will be printed for each of the following values for
ch
:'u'
,'o'
,'e'
?Answer:
The first
switch
statement correctly ends each case with abreak
to ensure that only the code directly associated with that case is executed. The secondswitch
statement does not end each case withbreak
and so execution will fall through from the matching case to all subsequent cases.The following output will be produced for the specified cases:
ch == 'u'
...S1
prints"you "
...S2
prints"you "
ch == 'o'
...S1
prints"ohh "
...S2
prints"ohh you "
ch == 'e'
...S1
prints"eee "
...S2
prints"eee eye ohh you "
-
Consider the following function to convert a number in the range 0..6 into a weekday name. 0 returns "Sun", 1 returns "Mon", 2 returns "Tue", and so on.
char *numToDay(int n) { assert(0 <= n && n <= 6); char *day; if (n == 0) { day = "Sun"; } else if (n == 1) { day = "Mon"; } else if (n == 2) { day = "Tue"; } else if (n == 3) { day = "Wed"; } else if (n == 4) { day = "Thu"; } else if (n == 5) { day = "Fri"; } else if (n == 6) { day = "Sat"; } return day; }
Suggest at least two alternative and more concise ways of achieving the same result. Include the
assert(...)
in each case.For each function, including the one above, explain what would happen if the
assert(...)
was removed and the function was invoked vianumToDay(7)
.Answer:
One possibility: use a
switch
...char *numToDay(int n) { assert(0 <= n && n <= 6); char *day; switch (n) { case 0: day = "Sun"; break; case 1: day = "Mon"; break; case 2: day = "Tue"; break; case 3: day = "Wed"; break; case 4: day = "Thu"; break; case 5: day = "Fri"; break; case 6: day = "Sat"; break; } return day; }
which could be done more compactly, and with a default, as...
char *numToDay(int n) { assert(0 <= n && n <= 6); switch (n) { case 0: return "Sun"; case 1: return "Mon"; case 2: return "Tue"; case 3: return "Wed"; case 4: return "Thu"; case 5: return "Fri"; case 6: return "Sat"; default: return "???"; } }
An alternative would be to use a table lookup...
char *numToDay(int n) { assert(0 <= n && n <= 6); char *days[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; return days[n]; }
-
Give an
if
statement equivalent to the followingswitch
statement:assert(islower(ch)); switch (ch) { case 'a': case 'e': case 'i': case 'o': case 'u': printf("vowel"); break; default: printf("consonant"); break; }
Answer:
The following
if
statement behaves the same:if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u') { printf("vowel"); } else { printf("consonant"); }
-
Use a conditional expression to replace the if in the following code:
int ch = getchar(); char *type; if (isdigit(ch)) { type = "digit"; } else { type = "non-digit"; } printf("'%c' is a %s\n", ch, type);
Answer:
int ch = getchar(); char *type = isdigit(ch) ? "digit" : "non-digit"; printf("'%c' is a %s\n", ch, type);
-
Consider the following definitions:
typedef int Integer; struct point { int x; int y; }; typedef struct point Point; struct node { int value; struct node *next; }; typedef struct node *Node;
Explain the meaning of each of the
typedef
s.Answer:
typedef
is a keyword which is used to give types alternate names. In the above code:Integer
is an alternate name forint
, for example the following two lines are equivalent:int a = 0; Integer a = 0;
Point
is an alternate name forstruct point
, for example the following two lines are equivalent:struct point p = {1, 2}; Point p = {1, 2};
Node
is an alternate name forstruct node *
, for example the following two lines are equivalent:
struct node *curr = NULL; Node curr = NULL;
Linked List Revision
-
Consider the following two list representations:
// Representation 1 struct node { int value; struct node *next; }; typedef struct node *List;
// Representation 2 struct node { int value; struct node *next; }; struct list { struct node *head; }; typedef struct list *List;
- Compare the two representations diagramatically.
- How is an empty list represented in each case?
- Suppose we want to write a function that inserts a number into a list at a given position. What would the function prototype look like for each representation?
- What are the advantages of having a separate list struct as in Representation 2?
Answer:
-
Representation 1:
Representation 2:
-
Representation 1: An empty list is simply represented by a
NULL
pointer.
Representation 2: An empty list is represented by a pointer to a
struct list
that contains aNULL
pointer in itshead
field.
-
Representation 1: The function must return a pointer to the first node of the updated list, since the new number could have been inserted in the beginning of the list.
Representation 2: The function does not need to return a value, since thestruct node *insert(struct node *list, int num, int pos);
head
pointer is hidden behind thestruct list
pointer, which does not change.
void insert(struct list *list, int num, int pos);
-
Some advantages:
Functions that modify the list don't need to return the updated list.
The list struct can be used to store additional information about the list (metadata), which can improve performance. For example, if the list struct contained a field for the length of the list, a function that gets the length of the list could simply return this value instead of iterating through the entire list. If the list struct contained a pointer to the last node of the list (usually calledlast
ortail
), then this pointer can be used to easily insert items at the end of the list. The tradeoff is that these fields need to be constantly updated to be consistent with the list.
-
Consider the following simple linked-list representation and a function that sums the values in the list:
typedef struct node { int value; struct node *next; } Node; typedef Node *List; // pointer to first node
Write a function to sum the values in the list. Implement it first using
while
and then usingfor
. Finally, implement it using recursion (can leave this for next week).Answer:
Version using
while
:int sumList(List l) { Node *curr = l; int sum = 0; while (curr != NULL) { sum += curr->value; curr = curr->next; } return sum; }
Version using
for
:int sumList(List l) { Node *curr; int sum = 0; for (curr = l; curr != NULL; curr = curr->next) { sum += curr->value; } return sum; }
Recursive version:
int sumList(List l) { if (l == NULL) { return 0; } else { return l->value + sumList(l->next); } }
-
Implement a function to delete the first instance of a value from a list, if it exists. Use the following list representation and prototype:
struct node { int value; struct node *next; }; struct list { struct node *head; }; void listDelete(struct list *l, int value);
Answer:
void listDelete(struct list *l, int value) { // list is empty if (l->head == NULL) { return; // deleting first value } else if (l->head->value == value) { struct node *newHead = l->head->next; free(l->head); l->head = newHead; // deleting middle value } else { struct node *curr = l->head; while (curr->next != NULL) { if (curr->next->value == value) { struct node *toDelete = curr->next; curr->next = toDelete->next; free(toDelete); break; } curr = curr->next; } } }
C Revision
-
Consider a program called
myprog
which is invoked as:./myprog hello there, 'John Shepherd' > myFile
- What are the values of
argc
andargv
? - Where would the statement
printf("%s", argv[1]);
place its output? - Where would the statement
ch = getchar();
get its input?
Answer:
argc
has the value 4;argv[0]
is"./myprog"
,argv[1]
is"hello"
,argv[2]
is"there,"
,argv[3]
is"John Shepherd"
printf
would place its output in the filemyFile
getchar()
would get its input from the keyboard
- What are the values of
-
Consider the following two sets of definitions and statements:
int x, y; char *c, *d, *e, *f; y = 2; x = y; d = "abc"; c = d; e = "xyz"; f = "xyz"; x++; *c = 'A';
- Show the state of the memory after the first set of assignments.
- Would anything be different if we had done
c = "abc"; d = c;
? - Show the state of memory after
x++;
. - What happens when
*c = 'A';
is executed? Why?
Answer:
The following diagram shows the memory layout up to
*c = 'A';
:Note: Since
e
andf
are assigned string literals which are identical (i.e., contain the same sequence of characters), the compiler may choose to create only one copy of the string and point bothe
andf
to the same string to save space.When the program attempts to do
*c = 'A';
, it generates a segmentation fault. Constant strings (e.g."xyz"
) are placed in a read-only area of the memory, and attempting to write into a read-only memory region fails.
Before starting this week's lab, make sure you properly understand the following topics:
- Structs (see videos on structs)
- Pointers (see videos on pointers)
- Malloc (see videos on malloc)
- Linked Lists (see videos on linked lists)