Assignment #7 Answers

Introductory C Programming

UW Experimental College

Assignment #7 ANSWERS


Question 1. What do we mean by the ``equivalence between pointers and arrays'' in C?

The cornerstone of the equivalence is that whenever we mention an array in an expression where it might seem that the array's ``value'' is needed, the compiler automatically generates a pointer to the array's first element. Other parts of the equivalence are that the array subscript notation [] works on both pointers and arrays, that an array can seemingly be assigned to a pointer, that when an array is seemingly passed to a function, a pointer to its first element is passed instead, and that when a function parameter is seemingly declared as an array, the compiler quietly declares it as a pointer, instead.

Question 2. If p is a pointer, what does p[i] mean?

It means the same as *(p+i), or, in other words, the contents of the object i locations past the one pointed to by p.

Question 3. Can you think of a few reasons why an I/O scheme which did not make you open a file and keep track of a ``file pointer,'' but instead let you just mention the name of the file as you were reading it, might not work as well?

There are two main reasons.

The first is that an operating system typically has to do a fair amount of work when you access a file: parse the filename, determine the actual location of the file within the storage system (disk, etc.), determine if you have permission to access the file, etc. If the filename were re-specified with each read or write call, the operating system might have to re-do that work each time. If, instead, the file is ``opened'' just once, the operating system can easily remember where the file is and everything it needs to know about it. It can arrange that the file descriptor or file pointer (which it returns to you from the open call and which it asks you to supply along with each read or write call) will serve as a reference to its memory of the necessary information.

The more important reason is that an open file needs some context: in particular, the location within the file where the next reading or writing will take place. If each time you read from a file, you just specified the file name, how would the system know not to start reading from the beginning of the file each time?

Question 4. If argc is 2, what is argv[1]? What if argc is 1?

If argc is 2, argv[1] is the first (and only) command line argument. If argc is 1, there are no command line arguments, so argv[1] doesn't point anywhere.

Exercise 1. Rewrite the replace function from last week to remove the restriction that the replacement substring have the same size.

If the replacement substring is a different size, we'll have to move the rest of the string right or left, either to make room or to close up the gap. Here's code to do that, by shifting the characters, one at a time. (Notice that when we're shifting to the right, we have to work our way through the characters we're shifting from right to left, to avoid stepping on characters we haven't shifted yet. Notice that we also have to worry about the new position of the '\0' string terminator in each case.)

void replace(char string[], char *from, char *to)
{
	int fromlen = strlen(from);
	int tolen = strlen(to);
	char *start, *p1, *p2;
	for(start = string; *start != '\0'; start++)
		{
		p1 = from;
		p2 = start;
		while(*p1 != '\0')
			{
			if(*p1 != *p2)
				break;
			p1++;
			p2++;
			}
		if(*p1 == '\0')
			{
			if(fromlen > tolen)
				{
				/* move rest of string left */
				p2 = start + tolen;
				for(p1 = start + fromlen; *p1 != '\0'; p1++)
					*p2++ = *p1;
				*p2 = '\0';
				}
			else if(fromlen < tolen)
				{
				/* move rest of string right */
				int leftover = strlen(start);
				p2 = start + leftover + (tolen - fromlen);
				*p2-- = '\0';
				for(p1 = start + leftover - 1;
						p1 >= start + fromlen; p1--)
					*p2-- = *p1;
				}
			for(p1 = to; *p1 != '\0'; p1++)
				*start++ = *p1;
			return;
			}
		}
}
As it happens, there's a function in the standard library, memmove, whose job it is to move characters (which might be strings) from point a to point b, taking care to make the copy in the right order if the source and destination strings overlap. memmove is declared in <string.h>, and its prototype looks approximately like this:
	memmove(char *dest, char *src, int n);
memmove is therefore a lot like strcpy (the dest and src arguments are in that order so that a call mimics an assignment). We can simplify our new version of replace by calling memmove instead:
#include <string.h>

void replace(char string[], char *from, char *to)
{
	int fromlen = strlen(from);
	int tolen = strlen(to);
	char *start, *p1, *p2;
	for(start = string; *start != '\0'; start++)
		{
		p1 = from;
		p2 = start;
		while(*p1 != '\0')
			{
			if(*p1 != *p2)
				break;
			p1++;
			p2++;
			}
		if(*p1 == '\0')
			{
			if(fromlen != tolen)
				{
				memmove(start + tolen, start + fromlen,
					strlen(start + fromlen) + 1);
							     /* + 1 for \0 */
				}
			for(p1 = to; *p1 != '\0'; p1++)
				*start++ = *p1;
			return;
			}
		}
}

Exercise 2. Rewrite the pattern matching program to prompt the user.

We'll need to use fgetline (see the notes) instead of getline, so that we can read lines from the file pointer connected to the file we open. With fgetline in hand, we rewrite the pattern-matching program to prompt for the file name and pattern, open the file, and finally call fgetline instead of getline:

#include <stdio.h>
#include <string.h>

#define MAXLINE 100

extern int getline(char [], int);
extern int fgetline(FILE *, char [], int);

int main()
{
	char line[MAXLINE];
	char filename[MAXLINE];
	char pat[MAXLINE];
	FILE *ifp;

	printf("file name? ");
	fflush(stdout);
	getline(filename, MAXLINE);

	printf("pattern? ");
	fflush(stdout);
	getline(pat, MAXLINE);

	ifp = fopen(filename, "r");
	if(ifp == NULL)
		{
		fprintf(stderr, "can't open %s\n", filename);
		exit(1);
		}

	while(fgetline(ifp, line, MAXLINE) != EOF)
		{
		if(strstr(line, pat) != NULL)
			printf("%s\n", line);
		}

	return 0;
}
The calls to fflush(stdout) ensure that the prompts show on the screen before we start reading the reply. This program is imperfect in that it does not check the return value from the two getline calls. If the user doesn't type a file name or pattern, the program will misbehave.

Exercise 3. Rewrite the pattern matching program to accept arguments from the command line.

#include <stdio.h>
#include <string.h>

#define MAXLINE 100

extern int fgetline(FILE *, char [], int);

int main(int argc, char *argv[])
{
	char line[MAXLINE];
	char *filename;
	char *pat;
	FILE *ifp;

	if(argc != 3)
		{
		fprintf(stderr, "missing search pattern or file name\n");
		exit(1);
		}

	pat = argv[1];
	filename = argv[2];

	ifp = fopen(filename, "r");
	if(ifp == NULL)
		{
		fprintf(stderr, "can't open %s\n", filename);
		exit(1);
		}

	while(fgetline(ifp, line, MAXLINE) != EOF)
		{
		if(strstr(line, pat) != NULL)
			printf("%s\n", line);
		}

	return 0;
}

It is useful to allow the program to search for the pattern in multiple files, or, if no file names are specified on the command line, to read from standard input. Here's how we might implement that. We break the read-lines-and-search loop out into a separate function, fsearchpat. We either call fsearchpat once, handing it stdin as a file pointer, or else we loop over all of the file names on the command line, opening each one and calling fsearchpat with that file pointer.

#include <stdio.h>
#include <string.h>

#define MAXLINE 100

int fsearchpat(FILE *, char *);

extern int fgetline(FILE *, char [], int);

int main(int argc, char *argv[])
{
	char *filename;
	char *pat;
	FILE *ifp;

	if(argc < 2)
		{
		fprintf(stderr, "missing search pattern\n");
		exit(1);
		}

	pat = argv[1];

	if(argc == 2)
		fsearchpat(stdin, pat);
	else	{
		int i;
		for(i = 2; i < argc; i++)
			{
			filename = argv[i];

			ifp = fopen(filename, "r");
			if(ifp == NULL)
				{
				fprintf(stderr, "can't open %s\n", filename);
				exit(1);
				}

			fsearchpat(ifp, pat);

			fclose(ifp);
			}
		}

	return 0;
}

int fsearchpat(FILE *ifp, char *pat)
{
	char line[MAXLINE];

	while(fgetline(ifp, line, MAXLINE) != EOF)
		{
		if(strstr(line, pat) != NULL)
			printf("%s\n", line);
		}

	return 0;
}


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