Final Review

March 16 2002

problem 1: printf

what do the following printf statements output?

printf("%1d\n%2d\n%3d\n", 24, 24, 24);
printf("%.2lf\n%4.1lf\n%6.4lf\n", 1.6666, 1.6666, 1.6666);

answer:

if you put a 1 right after the % in a format specifier, you're telling printf that you want the minimum width to be 1 character. in our case, the %1d is used to print 24, which is represented with two characters on the screen ['2' and '4']. since 24 is printed with two characters, and we told printf that the minimum width is 1 character, nothing interesting happens for this example, since we are printing two characters, and our format specifier indicates that we want the minimum width to be 1 character [and 2 is bigger than 1]. so the first line of output will be:

24

let's look at the second format specifier. we said %2d. this tells printf that we want the minimum width to be 2 characters. we're printing 24 again, which is printed with two characters, so nothing interesting happens, because we are printing two characters, and our minimum width is 2 [and 2 is equal to 2]. the second line of output is:

24

let's look at the third format specifier. something interesting happens this time, i promise. we said %3d. we're printing 24 again, which is still 2 characters, but our format specifier indicates that we want at least 3 characters of output. in these situations, printf will add spaces in front of the output to reach the minimum width. in our case, one space will be added, because we need one more character to reach the minimum width [2+1 = 3]. so, the third line of output is:

 24

let's look at the second printf statement. when you put a decimal point and a number after the %, it tells the computer how many digits after the decimal point you want to see. since we said %.2lf, we're saying that we want two digits after the decimal point. so the first line of output for this printf statement will be:

1.67

note that printf rounds. if i said printf("%.2lf\n", 1.543), the output would be 1.54.

let's look at the next format specifier. we ask for %4.1lf, which means we want the minimum width to be 4, and we want 1 digit after the decimal point. so the output is:

 1.7

one space is added, because it takes 3 characters to print 1.7.

the final format specifier is %6.4lf. this says we want the minimum width to be 6 characters, and we want 4 digits after the decimal point. the output is:

1.6666

no spaces are added, because it takes 6 characters to print 1.6666

so, to summarize, the complete output is:

24
24
 24
1.67
 1.7
1.6666

problem 2: loops

what is the output of this program?

#include <stdio.h>

void main()
{
  int i;
  for(i=3; i < 10; i++)
  {
    printf("%d\n", i);
    if(i > 4)
    {
      printf("break\n");
      break;
    }
  }
  while(i > 2)
  {
    printf("%d\n", i);
    i--;
  }
  printf("done\n%d\n", i);
}

answer:

let's trace through this program. the for loop says that i starts at 3. 3 is less than 10, so we go into the body of the loop. we print out 3, then we check if 3 is greater than 4. it's not, so we increase i by one [it's now 4], and we go into the body again, since 4 is less than 10. we print out 4. now we check if 4 is greater than 4. it's not, so we increase i by one [it's now 5], and we go into the body again, since 5 is less than 10. we print out 5, and check if 5 is bigger than 4. it is, so we print out "break", then we break.

the break takes us to the while loop. we check if i is greater than 2 [it is, i is currently 5], so we go into the body of the while loop, where we print out 5, then decrease i by one [so i is now 4]. we check if 4 is greater than 2 [it is], so we go into the body again, where we print out 4, and decrease i by one again [so i is now 3]. 3 is still greater than 2, so we go into the body again, print out 3, and decrease the value of i by one [so it's now 2]. 2 is not greater than 2, so we print out "done", followed by the final value of i [2], and we're done.

so, the output of this program is:

3
4
5
break
5
4
3
done
2

problem 3: functions

this program accepts three integers as input. describe the output of this program in one sentence [in other words, this program does something meaningful. what does this program do?].

#include <stdio.h>

int max(int a, int b);

void main()
{
  int a,b,c;
  scanf("%d %d %d", &a, &b, &c);
  printf("%d\n", max(a, max(b, c)));
}

int max(int a, int b)
{
  if(a > b)
    return a;
  else
    return b;
}

answer:

let's take a look at the definition of max first. from looking at the first line of the function definition, it accepts two integers [a and b] as input, and outputs an integer. the body of the function indicates that it returns a if a is bigger than b, and returns b otherwise.

so if we think about this a bit more, it looks like the max function takes two integers, and returns us the larger one.

now let's take a look at main. it reads three integers from the keyboard, a, b, and c, and prints out max(a,max(b,c)). let's see what this does, working inside to outside. max(b,c) will return the larger of b and c. the output of max(b,c) becomes one of the inputs of the other call to max, so max(a,max(b,c)) will return the larger of a and (the larger of b and c).

if we think about this a little more, main will print out the largest of the three integers we input.

problem 4: pointers and functions

what is the output of this program?

#include <stdio.h>

int* vooble(int* a, int* b);

void main()
{
  int a,b;
  int* c;
  a=5;
  b=7;
  c = vooble(&a, &b);

  printf("%d %d %d\n", a, b, *c);
}

int* vooble(int* a, int* b)
{
  int* c;

  c = b;
  b = a;
  a = c;

  *a = *a + 5;
  *b = *a + *b;  

  printf("%d %d %d\n", *a, *b, *c);

  return c;

  printf("vooble\n");
}

answer:

sorry, but i don't have time to draw pictures this time. i'll try to explain in words.

in main's scope, there are two integer variables, a and b, in addition to a pointer to an integer called c. a initially contains the number 5, and b contains the number 7. we don't know what c contains until we do the function call to vooble.

when we call vooble, we pass it the addresses of a and b as input. this means that the pointer called a in vooble's scope points to the a in main's scope, and the b in vooble's scope points to the b in main's scope.

in vooble, we see that we need an additional pointer to an integer called c. after that, we have three assignment statements. we see that the value of c becomes the same as the value of b. since both c and b are pointers, this means that c points to the same thing that b points to.

the next line says that b points to the same thing that a points to, and the line after that says that a points to the same thing that c points to. after we do these three assignment statements, we see that a and c point at the variable called b in main, and b points to the variable called a in main.

next, we have two more assignment statements. the first says that the variable pointed at by a must be increased by 5. since a points at b in main, and the b in main contains the value 7, the b in main now contains the value 12.

the next line says that the variable pointed at by b must be increased by *a. *a means that we need to look at the value of the variable pointed to by a, which is 12. so we need to increase the value of the variable pointed at by b by 12. since b points at the a in main, which is 5, the a in main now has value 17.

next we print out the values of *a, *b, and *c. we know that a points at b in main, b points at a in main, and c points at b in main, so we see 12 17 12.

finally, we return c. this means that the c in main will have the same value as the c in vooble, because we said c=vooble(&a,&b) back in main. since both c's are pointers, this means that the c in main will point to the same thing that the c in vooble points to. the c in vooble points at the b in main, so the c in main also points at the b in main.

we do not see "vooble" anywhere in the output, because that printf statement is after the return.

back in main, we print out the values of a, b, and *c. a and b are 17 and 12, and c points at b, so we see 17 12 12.

to summarize, the output is:

12 17 12
17 12 12

problem 5: arrays

what is the output of this program given this input? 2 5 3 4 1

#include <stdio.h>

int isgood(int x);

void main()
{
  int a[5], i;

  for(i=0; i < 5; i++) 
    scanf("%d", &(a[i]));

  for(i=0; i < 5; i++)
    if(isgood(a[i]))
      printf("%d %d\n", i, a[i]);
}

int isgood(int i)
{
  if(i % 2 == 0)
    return 1;
  else
    return 0;
}

answer:

let's start with the isgood function. it accepts an integer i as input, and returns an integer. the if statement checks if the remainder of i divided by 2 is equal to zero. in other words, it's checking if i is even. if i is even, the function returns 1, otherwise, it returns 0. don't forget that 0 is considered false, and everything else is considered true.

now let's look at main. first we create an array of 5 integers called a, and then we create a single integer called i. the first for loop reads integers from the keyboard, and puts them into the array at positions 0,1,2,3,4. in other words, the first for loop fills the array with the numbers 2,5,3,4,1.

the next loop runs the isgood function on the numbers in the array at positions 0,1,2,3,4, and if the function returns true, it prints out the index number, and the contents of the array at that index number. so the output is:

0 2
3 4

problem 6: more fun with arrays

the following program is supposed to accept five integers as input, and output the largest integer. complete the program.

#include <stdio.h>
#define SIZE 5

int max(int[]);

void main()
{
  int a[SIZE];
  int i;

  for(i=0; i < SIZE; i++)
  {
    scanf("%d", &(a[i]));
  }
  printf("%d\n", max(a));
}

int max(int a[])
{
  /* fill this in */
}

answer:

from looking at the framework provided, we know that the input to the max function is an array that contains 5 integers read from the keyboard. the output of the max function is sent to the screen by printf, so the max function needs to output the largest number in the array if this program is going to work.

how do we find the largest number in an array? one idea is to keep track of the biggest number we have seen so far, and to compare each number in the array to the biggest we've seen so far. if the number we're looking at is bigger than the biggest one we've seen so far, that number becomes the new biggest. here's what the code looks like:

int max(int a[])
{
  int i;
  int biggest=a[0];
  for(i=0; i < 5; i++)
    if(a[i] > biggest)
      biggest = a[i];
  return biggest;
}

problem 7: malloc

draw a schematic diagram of memory after each statement executes. label the figure with variable names and contents of locations. if the value is unknown, use ? as the contents of that location. clearly show the number of bytes you are allocating.

int n=3;
int* p1,p2;
double* q1,q2;
p1 = (int*) malloc(n * sizeof(int));
p2 = &(p1[n-1]);
*p1 = 5;
p1[n-1] = 3;
q2 = (double*) malloc(n * sizeof(double));
q1 = &(q2[0]);
*q1 = 5.5;
q1[n-1] = *q2;

answer:

here's some useful information.

all pointers are 4 bytes [regardless of what type they point to]
char is 1 byte
int is 4 bytes
float is 4 bytes
double is 8 bytes

the above is almost always true. if you assume the above for the final, you'll be okay.

so how big is everything we create up above? n is an integer, so it's 4 bytes. p1, p2, q1, and q2 are all pointers so they are all 4 bytes. the first malloc asks for n * sizeof(int) bytes, so malloc will return us a 12-byte piece of memory [3 * 4]. the second malloc asks for n * sizeof(double) bytes, so malloc will return us a 24-byte piece of memory [3 * 8].

so where do the pointers point to? we know that p1 points to the beginning of the piece of memory that malloc returns. this means that we can use p1 as an array of 3 integers, since it points to the beginning of a piece of memory big enough to hold 3 integers. the next line makes p2 point to the last cell in the array.

when we say *p1=5, we are putting a 5 into the piece of memory pointed to by p1. since p1 points to the beginning of the 3 cells, we put the 5 into the first cell.

in general, if p1 is the name of an array, *p1 is the same thing as p1[0].

p1[n-1]=3 puts a 3 into the last cell of the array.

the next line allocates an array of 3 doubles, and q2 points to the beginning of the array. the next line makes q1 point to the same place that q2 points to.

in general, if q2 is the name of an array, q2 is the same thing as &(q2[0]).

the next line puts 5.5 into the first cell in the array, and the next line says that we want the value in the last cell of the array to be the same as the value in the first cell of the array.

problem 8: files

suppose the file "input.txt" contains the text hello my name is jeremy.

what is the output of the following program, and what is in the file "output.txt" after the program is run?

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

void main()
{
  FILE* in;
  FILE* out;
  char buf[256];
  int n;
  int total=0;

  in = fopen("input.txt", "r");
  out = fopen("output.txt", "w");

  while(1)
  {
    fscanf(in, "%s", buf);
    if(feof(in))
      break;
    n = strlen(buf);
    total = total + n;
    fprintf(out, "%d\n", n);
  }

  printf("%d\n", total);
}

answer:

let's see what this program does. it starts by creating a bunch of variables, including a file pointer called in that points to the beginning of the file called input.txt, and a file pointer called out that points to the beginning of the file called output.txt.

next, we go into an infinite loop [it's okay, because the break statement lets us escape]. each time we go through the loop, we grab a word from input.txt, determine the number of characters in the word, add the number of characters in the word to our total, and write the number of characters in the word to output.txt.

if we go past the end of the file while reading input.txt, we break out of the loop, and print the total number of characters to the screen.

so, when we run this program, we expect output.txt to contain:

5
2
4
2
6

because those are the numbers of letters in each word of input.txt.

we expect to see 19 on the screen, because that is the total number of characters in all the words.

problem 9: more malloc

the following program accepts an integer n as input, followed by n more integers. the program outputs the n integers in reverse order. for example, given the input 3 4 5 6, the program will output 6 5 4. n is the first integer, so in this example, n is 3. since n is 3, we receive 3 more integers as input, and we output those 3 integers in reverse order.

the program only works if n is less than 5. modify the program so that it works for any n.

#include <stdio.h>

#define SIZE 5

void main()
{
  int i;
  int n;
  int a[SIZE];

  scanf("%d", &n);

  for(i=0; i < n; i++)
  {
    scanf("%d", &(a[i]));
  }

  for(i=n-1; i >= 0; i--)
  {
    printf("%d\n", a[i]);
  }
}

answer:

to make this program work for any n, we need to replace the fixed-size array with a dynamically allocated one. the code looks like this:

#include <stdio.h>

void main()
{
  int i;
  int n;
  int* a;

  scanf("%d", &n);

  a = (int*) malloc(n * sizeof(int));

  for(i=0; i < n; i++)
  {
    scanf("%d", &(a[i]));
  }

  for(i=n-1; i >= 0; i--)
  {
    printf("%d\n", a[i]);
  }

  free(a);
}

so now we say that a is a pointer to a piece of memory returned by malloc. we ask malloc for enough space to hold n integers. after doing the malloc, we can use a just like any other array of integers, because the name of an array is just a pointer to the beginning of the array, and a points to a piece of memory big enough to hold n integers, so we can use a as an array.

since we can use a as an array, the two for loops are unchanged.

at the end, we free a, to tell the computer that we don't need the piece of memory that malloc found for us anymore.