Assignment #5 Answers

Introductory C Programming

UW Experimental College

Assignment #5 ANSWERS


Question 1. What's wrong with #define N 10; ?

The semicolon at the end of the line will become part of N's definition, which is hardly ever what you want.

Question 2. Suppose you had the definition #define SIX 2*3 . What value would the declaration int x = 12 / SIX; initialize x to?

18. (18? How could it be 18? It sure looks like it should be 2, doesn't it?)

The preprocessor performs a simple textual substitution; it knows nothing about operator precedence, or even much about the syntax of C. After the preprocessor substitutes the value of the macro SIX, the declaration looks like

	int x = 12 / 2*3;
Since multiplication and division ``group'' (or ``associate'') from left to right, this is interpreted as
	int x = (12 / 2) * 3;
To guard against these ``surprises,'' it's a very good idea to parenthesize the values of macros which are not simple constants. In this case, a safer definition of the macro would have been
	#define SIX (2*3)
This way, when the preprocessor performs its simple textual substitution, the resulting expression is automatically parenthesized so that the compiler gives you the result you expect. (Of course, this hypothetical SIX macro is useless in any case, but the point is that whenever a macro's value is an expression of any kind, it needs extra parentheses to avoid surprises.)

Question 3. If the header file x.h contains an external prototype declaration for a function q(), where should x.h be included?

It should be included in each source file where q() is called, so that the compiler will see the prototype declaration and be able to generate correct code. It should also be included in the source file where q() is defined, so that the compiler will be able to notice, and complain about, any mismatches between the prototype declaration and the actual definition. (It's vital that the prototype which will be used where a function is called be accurate; an incorrect prototype is worse than useless.)

Question 4. How many differences can you think of between i and J?

The line

	int i = 10;
declares a conventional run-time variable, named i, initially containing the value 10. It will be possible to change i's value at run time. i may appear in expressions (i.e., its value may be fetched), but since it is not constant, it could not be used where C requires a constant, such as in the dimension of an array declaration.

The line

	#define J 10
on the other hand, defines a preprocessor macro named J having the value 10. For the rest of the current source file, anywhere you write a single J, the preprocessor will replace it with 10. An array declaration such as
	int a[J];
will be fine; it will be just as if you had written
	int a[10];
However, J exists only at compile time. It is not a run-time variable; if you tried to ``change its value'' at run time by writing
	J = 20;
it would be just as if you had written
	10 = 20;
and the compiler would complain.

(One more little difference is that the line int i = 10; ends in a semicolon, while the line #define J 10 does not.)

Exercise 2. Write a program to read its input and write it out, double-spaced.

This is easy if we realize that all we have to do is read the input a character at a time, copying each input character through to the output, except that whenever we see a '\n' character, write a second one out, too.

#include <stdio.h>

int main()
{
	int c;

	while((c = getchar()) != EOF)
		{
		putchar(c);
		if(c == '\n')
			putchar('\n');
		}

	return 0;
}
The program won't be too interesting if you type text at it interactively. If you're using a Unix or MS-DOS system, you can run it on a file by typing
	doublespace < filename
The < mechanism indicates that the program should be run with its ``standard input'' (i.e. the stream of characters read by getchar) connected to the file with the given filename, rather than to the keyboard.

Exercise 3. Write a function which counts the number of times a character appears in a string.

Here is the function. Notice that it is very similar to the mystrlen function in the notes, except that rather than counting all characters in the string, it only counts those matching the argument c.

int countnchars(char string[], int ch)
{
	int i;
	int count = 0;
	for(i = 0; string[i] != '\0'; i++)
		{
		if(string[i] == ch)
			count++;
		}
	return count;
}
Here is a tiny little main program, to test it out:
#include <stdio.h>

extern int countnchars(char string[], int ch);

int main()
{
	char string[] = "Hello, world!";
	char c = 'o';

	printf("The letter %c appears in \"%s\" %d times.\n",
		c, string, countnchars(string, c));
	return 0;
}

Exercise 4. Write a short program to read two lines of text, and concatenate them using strcat.

#include <stdio.h>
#include <string.h>	/* for strcpy and strcat */

#define MAXLINE 100

extern int getline(char [], int);

int main()
{
	char string1[MAXLINE], string2[MAXLINE];
	int len1, len2;
	char newstring[MAXLINE*2];

	printf("enter first string:\n");
	len1 = getline(string1, MAXLINE);
	printf("enter second string:\n");
	len2 = getline(string2, MAXLINE);

	if(len1 == EOF || len2 == EOF)
		exit(1);

	strcpy(newstring, string1);
	strcat(newstring, string2);

	printf("%s\n", newstring);

	return 0;
}

Exercise 5. Write a function to find a substring in a larger string and replace it with a different substring.

Here is one way. (Since the function doesn't return anything, I've defined it with a return type of void.)

void replace(char string[], char from[], char to[])
{
	int start, i1, i2;
	for(start = 0; string[start] != '\0'; start++)
		{
		i1 = 0;
		i2 = start;
		while(from[i1] != '\0')
			{
			if(from[i1] != string[i2])
				break;
			i1++;
			i2++;
			}
		if(from[i1] == '\0')
			{
			for(i1 = 0; to[i1] != '\0'; i1++)
				string[start++] = to[i1];
			return;
			}
		}
}
This code is very similar to the mystrstr function in the notes, chapter 10, section 10.4, p. 8. (Since strstr's job is to find one string within another, it's a natural for the first half of replace.)

Extra credit: Think about what replace() should do if the from string appears multiple times in the input string.

Our first implementation replaced only the first occurrence (if any) of the from string. It happens, though, that it's trivial to rewrite our first version to make it replace all occurrences--just omit the return after the first string has been replaced:

void replace(char string[], char from[], char to[])
{
	int start, i1, i2;
	for(start = 0; string[start] != '\0'; start++)
		{
		i1 = 0;
		i2 = start;
		while(from[i1] != '\0')
			{
			if(from[i1] != string[i2])
				break;
			i1++;
			i2++;
			}
		if(from[i1] == '\0')
			{
			for(i1 = 0; to[i1] != '\0'; i1++)
				string[start++] = to[i1];
			}
		}
}


This page by Steve Summit // Copyright 1995-9 // mail feedback