Chapter 3 – Control Flow
Exercise 3-1
Our binary search makes two tests inside the loop, when one would suffice (at the price of more tests outside). Write a version with only one test inside the loop and measure the difference in run-time.
#include <stdio.h>
#include <time.h>
#define MAX_ELEMENT 20000
/* Original K&R function */
int binsearch(int x, int v[], int n);
/* Our new function */
int binsearch2(int x, int v[], int n);
/* Outputs approximation of processor time required
for our two binary search functions. We search for
the element -1, to time the functions' worst case
performance (i.e. element not found in test data) */
int main(void) {
int testdata[MAX_ELEMENT];
int index; /* Index of found element in test data */
int n = -1; /* Element to search for */
int i;
clock_t time_taken;
/* Initialize test data */
for ( i = 0; i < MAX_ELEMENT; ++i )
testdata[i] = i;
/* Output approximation of time taken for
100,000 iterations of binsearch() */
for ( i = 0, time_taken = clock(); i < 100000; ++i ) {
index = binsearch(n, testdata, MAX_ELEMENT);
}
time_taken = clock() - time_taken;
if ( index < 0 )
printf("Element %d not found./n", n);
else
printf("Element %d found at index %d./n", n, index);
printf("binsearch() took %lu clocks (%lu seconds)/n",
(unsigned long) time_taken,
(unsigned long) time_taken / CLOCKS_PER_SEC);
/* Output approximation of time taken for
100,000 iterations of binsearch2() */
for ( i = 0, time_taken = clock(); i < 100000; ++i ) {
index = binsearch2(n, testdata, MAX_ELEMENT);
}
time_taken = clock() - time_taken;
if ( index < 0 )
printf("Element %d not found./n", n);
else
printf("Element %d found at index %d./n", n, index);
printf("binsearch2() took %lu clocks (%lu seconds)/n",
(unsigned long) time_taken,
(unsigned long) time_taken / CLOCKS_PER_SEC);
return 0;
}
/* Performs a binary search for element x
in array v[], which has n elements */
int binsearch(int x, int v[], int n) {
int low, mid, high;
low = 0;
high = n - 1;
while ( low <= high ) {
mid = (low+high) / 2;
if ( x < v[mid] )
high = mid - 1;
else if ( x > v[mid] )
low = mid + 1;
else
return mid;
}
return -1;
}
/* Implementation of binsearch() using
only one test inside the loop */
int binsearch2(int x, int v[], int n) {
int low, high, mid;
low = 0;
high = n - 1;
mid = (low+high) / 2;
while ( low <= high && x != v[mid] ) {
if ( x < v[mid] )
high = mid - 1;
else
low = mid + 1;
mid = (low+high) / 2;
}
if ( x == v[mid] )
return mid;
else
return -1;
}
Exercise 3-2
Write a function escape(s,t) that converts characters like newline and tab into visible escape sequences like /n and /t as it copies the string t to s . Use a switch. Write a function for the other direction as well, converting escape sequences into the real characters.
/*
Write a function escape(s,t) that converts characters
like newline and tab into visible escape sequences like
/n and /t as it copies the string t to s . Use a switch.
Write a function for the other direction as well,
converting escape sequences into the real characters.
*/
#include <stdio.h>
#define MAX_LEN 1024
#define SLASH 1
#define TAB 2
#define NEWLINE 3
#define OTHER 4
/* escape: converts char like newline and tab into visible
escape sequences like /n and /t as it copies the string t to s*/
void escape(char s[], char t[]);
/* deescape: converse of escape */
void deescape(char t[], char s[]);
int main() {
char str[] = "This is a test/n. This is another example./n";
char t[MAX_LEN];
char str2[MAX_LEN];
escape(str, t);
printf("%s/n", str);
printf("%s/n", t);
deescape(t, str2);
printf("%s/n", str2);
return 0;
}
void escape(char s[], char t[]) {
int i, j;
for (i = 0, j = 0; s[i] != '/0'; i++) {
if (s[i] == '/t' ) {
t[j++] = '//';
t[j++] = 't';
} else if (s[i] == '/n') {
t[j++] = '//';
t[j++] = 'n';
}
else {
t[j++] = s[i];
}
}
t[j] = '/0';
}
void deescape(char t[], char s[]) {
int state = OTHER, i, j;
for (i = 0, j = 0; t[i] != '/0'; i++) {
switch(state) {
case OTHER:
if (t[i] == '//') {
state = SLASH;
} else {
s[j++] = t[i];
}
break;
case SLASH:
if (t[i] == 't') {
state = TAB;
} else if (t[i] == 'n') {
state = NEWLINE;
} else if(t[i] != '//') {
state = OTHER;
}
break;
case TAB:
s[j++] = '/t';
state = OTHER;
break;
case NEWLINE:
s[j++] = '/n';
state = OTHER;
break;
default: break;
}
}
s[j] = '/0';
}
Exercise 3-3
Write a function expand(s1,s2) that expands shorthand notations like a-z in the string s1 into the equivalent complete list abc...xyz in s2 . Allow for letters of either case and digits, and be prepared to handle cases like a-b-c and a-z0-9 and -a-z . Arrange that a leading or trailing - is taken literally.
/*
EX3_3.C
=======
Suggested solution to Exercise 3-3
By: Paul Griffiths
Email: paul@paulgriffiths.demon.co.uk
*/
#include <stdio.h>
#include <string.h>
void expand(char * s1, char * s2);
int main(void) {
char *s[] = { "a-z-", "z-a-", "-1-6-",
"a-ee-a", "a-R-L", "1-9-1",
"5-5", NULL };
char result[100];
int i = 0;
while ( s[i] ) {
/* Expand and print the next string in our array s[] */
expand(result, s[i]);
printf("Unexpanded: %s/n", s[i]);
printf("Expanded : %s/n", result);
++i;
}
return 0;
}
/* Copies string s2 to s1, expanding
ranges such as 'a-z' and '8-3' */
void expand(char * s1, char * s2) {
static char upper_alph[27] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static char lower_alph[27] = "abcdefghijklmnopqrstuvwxyz";
static char digits[11] = "0123456789";
char * start, * end, * p;
int i = 0;
int j = 0;
/* Loop through characters in s2 */
while ( s2[i] ) {
switch( s2[i] ) {
case '-':
if ( i == 0 || s2[i+1] == '/0' ) {
/* '-' is leading or trailing, so just copy it */
s1[j++] = '-';
++i;
break;
}
else {
/* We have a "range" to extrapolate. Test whether
the two operands are part of the same range. If so,
store pointers to the first and last characters in
the range in start and end, respectively. If not,
output and error message and skip this range. */
if ( (start = strchr(upper_alph, s2[i-1])) &&
(end = strchr(upper_alph, s2[i+1])) )
;
else if ( (start = strchr(lower_alph, s2[i-1])) &&
(end = strchr(lower_alph, s2[i+1])) )
;
else if ( (start = strchr(digits, s2[i-1])) &&
(end = strchr(digits, s2[i+1])) )
;
else {
/* We have mismatched operands in the range, such as
'a-R', or '3-X', so output an error message, and just
copy the range expression. */
fprintf(stderr, "EX3_3: Mismatched operands '%c-%c'/n",
s2[i-1], s2[i+1]);
s1[j++] = s2[i-1];
s1[j++] = s2[i++];
break;
}
/* Expand the range */
p = start;
while ( p != end ) {
s1[j++] = *p;
if ( end > start )
++p;
else
--p;
}
s1[j++] = *p;
i += 2;
}
break;
default:
if ( s2[i+1] == '-' && s2[i+2] != '/0' ) {
/* This character is the first operand in a range,
so just skip it - the range will be processed in
the next iteration of the loop. */
++i;
}
else {
/* Just a normal character, so copy it */
s1[j++] = s2[i++];
}
break;
}
}
s1[j] = s2[i]; /* Don't forget the null character */
}
Exercise 3-4
In a two's complement number representation, our version of itoa does not handle the largest negative number, that is, the value of n equal to -(2 to the power (wordsize - 1)) . Explain why not. Modify it to print that value correctly regardless of the machine on which it runs.
/*
EX3_4.C
=======
Suggested solution to Exercise 3-4
Solution by Paul Griffiths - paul@paulgriffiths.demon.co.uk
Error spotted by Wayne Lubin and fixed. - wclubin@telocity.com
*/
#include <stdlib.h> /* abs() */
#include <stdio.h>
#include <string.h>
#include <limits.h>
void itoa2(int n, char s[]);
void reverse(char s[]);
int main(void) {
char buffer[20];
printf("INT_MIN: %d/n", INT_MIN);
itoa2(INT_MIN, buffer);
printf("Buffer : %s/n", buffer);
return 0;
}
void itoa2(int n, char s[]) {
int i, sign;
sign = n;
i = 0;
do {
s[i++] = abs(n % 10) + '0';
} while ( n /= 10 );
if (sign < 0)
s[i++] = '-';
s[i] = '/0';
reverse(s);
}
void reverse(char s[]) {
int c, i, j;
for ( i = 0, j = strlen(s)-1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
Exercise 3-5
Write the function itob(n,s,b) that converts the integer n into a base b character representation in the string s . In particular, itob(n,s,16) formats n as a hexadecimal integer in s .
/*
EX3_5.C
=======
Suggested solution to Exercise 3-5
Solution by Paul Griffiths - paul@paulgriffiths.demon.co.uk
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
void itob(int n, char s[], int b);
void reverse(char s[]);
int main(void) {
char buffer[10];
int i;
for ( i = 2; i <= 20; ++i ) {
itob(255, buffer, i);
printf("Decimal 255 in base %-2d : %s/n", i, buffer);
}
return 0;
}
/* Stores a string representation of integer n
in s[], using a numerical base of b. Will handle
up to base-36 before we run out of digits to use. */
void itob(int n, char s[], int b) {
static char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int i, sign;
if ( b < 2 || b > 36 ) {
fprintf(stderr, "EX3_5: Cannot support base %d/n", b);
exit(EXIT_FAILURE);
}
if ((sign = n) < 0)
n = -n;
i = 0;
do {
s[i++] = digits[abs(n % b)];
} while ((n /= b));
if (sign < 0)
s[i++] = '-';
s[i] = '/0';
reverse(s);
}
/* Reverses string s[] in place */
void reverse(char s[]) {
int c, i, j;
for ( i = 0, j = strlen(s)-1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
Exercise 3-6
Write a version of itoa that accepts three arguments instead of two. The third argument is a minimum field width; the converted number must be padded with blanks on the left if necessary to make it wide enough.
/*
EX3_6.C
=======
Suggested solution to Exercise 3-6
Solution by Paul Griffiths - paul@paulgriffiths.demon.co.uk
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#define MAX_LEN 64
void itoa3(int n, char s[], int width);
void reverse(char s[]);
int main(void) {
char buffer[MAX_LEN];
itoa3(INT_MIN, buffer,30);
printf("Buffer:%s/n", buffer);
return 0;
}
void itoa3(int n, char s[], int width) {
int i, sign;
if ((sign = n) < 0)
n = -n;
i = 0;
do {
s[i++] = abs(n % 10) + '0';
/* printf("%d %% %d + '0' = %d/n", n, 10, s[i-1]); */
} while ((n /= 10));
if (sign < 0)
s[i++] = '-';
while (i < width ) /* Only addition to original function */
s[i++] = ' ';
s[i] = '/0';
reverse(s);
}
void reverse(char s[]) {
int c, i, j;
for ( i = 0, j = strlen(s)-1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}