CS360 Midterm Exam. March 13, 2012. James S. Plank

Answers and Grading


Question 0

There are many ways to do this with strcpy(), sprintf(), a few for() loops, and even strcat(). If you use strcat(), however, you must be careful, because it will result in an O(n2) algorithm if you keep calling strcat() on the beginning of the string that you are constructing.

My solution uses strcpy(). The whole program, which calls atos() on argv is in q0.c:

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

char *atos(char **a)
{
  int i, size;
  char *rv;

  size = 1;
  for (i = 0; a[i] != NULL; i++) size += (strlen(a[i])+1);
  rv = (char *) malloc(sizeof(char)*size);

  size = 0;
  rv[0] = '\0';

  for (i = 0; a[i] != NULL; i++) {
    if (i != 0) {
      rv[size] = ' ';
      size++;
    }
    strcpy(rv+size, a[i]);
    size += strlen(a[i]);
  }
  return rv;
}

main(int argc, char **argv)
{
  char *s;

  s = atos(argv);
  printf("%s\n", s);
}

Grading

This one started at 15 points, and you received deductions for the following:


Question 1

lstat() gives you information about the inode of a file. In jtar, we use lots of this information:


Question 2

Each of these was worth a half a point.


Question 3

This is a matter of chasing pointers. Let's start with the first part of the code:

  for (i = 0; i < 5; i++) printf("%12d ", b[i]);
  printf("\n");
  printf("\n");

Since b's value is 0xbfffdb48, b[0] is the value at that address: -1073851180. b[1] is the value four bytes after that address, and so on. The output is:

 -1073751180  -1073751176         1611         7683        42335 
Now, look at the next two lines:

  for (i = 0; i < 5; i++) printf("%s\n", c[i]);
  printf("\n");

c's value is 0xbfffdb3c, so c[0] is 0xbfffdb60. Since we are printing c[0] as a string, we print the characters starting at 0xbfffdb60 and ending with the null character: "d|". Similarly, c[1] is 0xbfffdb68: "Binky". The output is:

d|
Binky
Luigi
FredDontonio
Dontonio
Now the next 7 lines:

  for (i = 0; i < 3; i++) {
    for (j = 0; j < 3; j++) {
      printf("%12d ", a[i][j]);
    }
    printf("\n");
  }
  printf("\n");

a[0] is the value at 0xbfffdb30: 0xbfffdb4c. Since a is an (int **), we treat that as a pointer: a[0][0] is the integer at 0xbfffdb4c: -1073751176, and a[0][1] is the integer four bytes later: 1611. a[0][2] is the integer four bytes after that: 7683.

a[1] is the value four bytes after 0xbfffdb30, which is at 0xbfffdb34. Its value is 0xbfffdb54. Thus, a[1][0] is the integer at 0xbfffdb54: 7683, and a[1][1] is four bytes later: 42335.

The output is:

 -1073751176         1611         7683 
        7683        42335        60605 
       31844        40554   1802398018 
Now look at the next four lines:

  for (b = a[0]; b < (int *) a[0][0]; b += 2) {
    printf("%12d\n", *b);
  }
  printf("\n");

This loop sets b to 0xbfffdb4c, and increments it by 8 bytes (pointer arithmetic) on each iteration. Since a[0][0] equals 0xbfffdb78, this will iterate quite a few times:

b*b
0xbfffdb4c-1073751176
0xbfffdb547683
0xbfffdb5c60605
0xbfffdb6440554
0xbfffdb6c1967915129
0xbfffdb741684369990

The final part of the code sets s to 0xbfffdb60 and t to 0xbfffdb61. At each iteration it sets s[0] to *t, and then increments t by seven:

t*t
0xbfffdb61'|'
0xbfffdb68'B'
0xbfffdb6f'u'
0xbfffdb76'e'
0xbfffdb7d'n'
0xbfffdb84'z'

Thus, s[0] is "|Buenz". The final word is the hex value at 0xbfffdb60. This is a little tricky. You can see that the hex value of four bytes is the hex value of the characters in reverse order. For example 31844 is equal 0x00007c64, which is equal to 'd', '|', '\0' and '\0'. Thus, 'd' is 0x64 and '|' is 0x7c. This means that *b is equal to '|' (0x7c), 'B' (0x42), 'u' (0x75) and 'e' (0x65) in reverse order: 0x6575427c.

The entire output is:

a: 0xbfffdb30
b: 0xbfffdb48
c: 0xbfffdb3c

 -1073751180  -1073751176         1611         7683        42335 

d|
Binky
Luigi
FredDontonio
Dontonio

 -1073751176         1611         7683 
        7683        42335        60605 
       31844        40554   1802398018 

 -1073751176
        7683
       60605
       40554
  1967915129
  1684369990

|Buenz 0x6575427c

To write this problem, I wrote a program that set up the stack, and then called messy_proc(), which printed the stack and then ran as on the test. If you run it on a lab machine, the output will be different, but it will be the same relative to a, b, and c. The program is in q3.c.

Grading

Each of the five sets of print statements was worth 3 points.


Question 4

The best thing to do with the question is to draw boxes around the regions of memory, starting with 0x1c230, which is 40 bytes long. When you're done with that, you identify the first free chunk as 0x1c280, which is 32 bytes long, and then you chase the flink pointers to identify the rest of the free chunks. I have done this in Q4-Memory-Colored.jpg, where the free chunks are blue and the allocated ones are yellow.

Once this is done, parts A and B are straightforward:

Part A:

Part B:

Part C: sbrk(0) returns the first byte after the heap: 0x1c3b0.

Part D: Memory is paged -- therefore, the void really doesn't start until 0x1d000 (4K pages), or at the very least 0x1c400 (1K pages). Since 0x1c3c4 is before both of those addresses, it will complete successfully with no segmentation violation.

Grading

5 points each for parts A and B. 2 points each for C and D.