2024 CS360 Midterm Exam

February 29, 2024. 75 Minutes.

This was an exam on Canvas. Several questions used question banks, so this is an example exam.

Unless otherwise stated, please assume that you are working on a little endian machine with four-byte pointers.


Question 1 - 0 Points

Please enter your UTK username. For example, mine is jplank.

Question 2 - 0 Points

If you feel compelled to write me a note about any of your answers, please do it here, rather than in the answer boxes of the various questions. You don't need to do this; however, I've learned that some students feel that it is necessary.

Question 3 - 6 Points

If you have questions about pointer size, etc, please read the preamble to this exam.
Please enter the output of this program
#include <stdio.h>

struct pebbles {
  char x1;
  short x2;
  int x3;
  double x4;
  void *v;
};

int main()
{
  int i;

  i = sizeof(struct pebbles);
  printf("%d\n", i);
  return 0;
}
[______________]
Please enter the output of this program
#include <stdio.h>

struct wilma {
  int x1;
  char x2;
};

int main()
{
  int i;

  i = sizeof(struct wilma);
  printf("%d\n", i);
  return 0;
}
[______________]
Please enter the output of this program
#include <stdio.h>

struct fred {
  char x1;
  int x2;
};

int main()
{
  int i;

  i = sizeof(struct fred);
  printf("%d\n", i);
  return 0;
}
[______________]

Question 4 - 14 Points

Recall that the prototypes of strcpy(), strcat() and memcpy() are:
char *strcpy(char *dst, const char *src);
char *strcat(char *dst, const char *src);
void *memcpy(void *dst, const void *src, size_t n);
Behold the following program in C:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main()
{
  char *x, *s, *t;;
  int i;

  s = (char *) malloc(sizeof(char) * 50);
  t = (char *) malloc(sizeof(char) * 50);

  //         012345678901234567890123
  strcpy(s, "KYFGUNDIWZPERCOVSTJQALXM");

  strcpy(t, "ABCDEFG");
  memcpy(t+3, s, 2);
  printf("1-%s\n", t);

  strcpy(t, "HIJKLMN");
  strcpy(t+3, s+20);
  printf("2-%s\n", t);

  //         012345678901234567890123
  strcpy(s, "KYFGUNDIWZPERCOVSTJQALXM");

  for (x = s; x - s < 23; x += 5) *x = '\0';
  printf("3-%s\n", s);
  printf("4-%s\n", s+7);
  printf("5-%s\n", s+14);
  printf("6-%s\n", s+21);

  strcat(s+1, "OPQR");
  printf("7-%s\n", s+1);
  return 0;
}
Please answer the following questions:

Question 5 - 0 Points

This was scratch space so you could cut-and-paste things.

Question 6 - 20 Points

Recall that the prototypes of strcpy() and memcpy() are:
char *strcpy(char *dst, const char *src);
void *memcpy(void *dst, const void *src, size_t n);
The ASCII value of the character 'A' is 0x41.

Behold the following program in C:

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

int main()
{
  unsigned int *r, *x, num;
  unsigned char *s;
  char *t;

  r = (unsigned int *) malloc(sizeof(unsigned int) * 4);
  s = (unsigned char *) r;

  num = 0x374a9a7c;

  for (x = r; x < r+4; x++) {
    *x = num;
    num += 0x11111111;
  }

  t = (char *) (r+1);
  strcpy(t, "BEAD");

  printf("0x%08x\n", *r);      /* Line 1 */
  printf("0x%08x\n", *(r+1));  /* Line 2 */
  printf("0x%08x\n", *(r+2));  /* Line 3 */
  printf("0x%08x\n", *(r+3));  /* Line 4 */
  printf("0x%02x\n", *s);      /* Line 5 */
  printf("0x%02x\n", *(s+5));  /* Line 6 */
  printf("0x%02x\n", *(s+10)); /* Line 7 */
  printf("0x%02x\n", *(s+15)); /* Line 8 */

  memcpy(&num, s+10, 4);
  printf("0x%x\n", num);       /* Line 9 */

  s += 20;
  x = (unsigned int *) s;
  printf("%ld\n", x-r);        /* Line 10 */
  
  return 0; 
} 
Please answer the following questions:

Question 7 - 0 Points

This was scratch space so you could cut-and-paste things.

Question 8 - 18 Points

We are running the following procedure:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

struct fred {
  int i;
  int *ip;
  int **ipp;
  struct fred *fp;
};

void proc_a(struct fred *f)
{
  printf("0x%02x\n", f->i & 0xff);                     /* Line 1 */
  printf("0x%02x\n", ((unsigned int) f->ip) & 0xff);   /* Line 2 */
  printf("0x%02x\n", *(f->ip) & 0xff);                 /* Line 3 */
  printf("0x%02x\n", **(f->ipp) & 0xff);               /* Line 4 */
  printf("0x%02x\n", f->fp->i & 0xff);                 /* Line 5 */
  printf("0x%02x\n", f->fp->fp->i & 0xff);             /* Line 6 */
  printf("0x%02x\n", f[1].i & 0xff);                   /* Line 7 */
  printf("0x%02x\n", f[2].i & 0xff);                   /* Line 8 */
  printf("0x%02x\n", f[3].fp->i & 0xff);               /* Line 9 */
}

When we run proc_a, the value of f is: 0x7685c1e8.

The contents of memory are as listed below:

 Address      Value
----------  ----------
0x7685c1e8  0x7685c20c
0x7685c1ec  0x7685c21c
0x7685c1f0  0x7685c208
0x7685c1f4  0x7685c224
0x7685c1f8  0x7685c1f8
0x7685c1fc  0x7685c220
0x7685c200  0x7685c1f4
0x7685c204  0x7685c228
0x7685c208  0x7685c230
0x7685c20c  0x7685c1f0
0x7685c210  0x7685c224
0x7685c214  0x7685c1f4
0x7685c218  0x7685c230
0x7685c21c  0x7685c1ec
0x7685c220  0x7685c1e8
0x7685c224  0x7685c1e8
0x7685c228  0x7685c220
0x7685c22c  0x7685c234
0x7685c230  0x7685c214
0x7685c234  0x7685c20c
Please answer the following questions: Remember, each answer is going to be one byte, like 0x58.

Question 9 - 0 Points

This was scratch space so you could cut-and-paste things.

Question 10 - 10 Points

I cd to a directory and do the following commands:
UNIX> ls -l
total 16
drwxr-xr-x  2 plank  staff   64 Feb 27 23:41 dir1
-rw-r--r--  1 plank  staff  638 Feb 27 23:34 f1.txt
-rw-r--r--  1 plank  staff  746 Feb 27 23:35 f2.txt
UNIX> ln f1.txt dir1/f3.txt
UNIX> echo fred >> f1.txt
UNIX> 
These actions have changed a few things on my disk. Please tell me, to the best of your knowledge in what I have taught you, exactly what has changed on the disk.

Question 11 - 16 Points

Write a program that prints all files in the current directory, ordered from largest to smallest. If multiple files have the same size, then print them lexicographically. Do not bother with include files. You may assume that no file is larger than 230 bytes.

For example:

UNIX> ls -la
total 32
drwxr-xr-x   6 plank  staff   192 Feb 27 20:45 .
drwxr-xr-x  29 plank  staff   928 Feb 27 20:45 ..
-rw-r--r--   1 plank  staff  1570 Feb 27 20:45 aaa.txt
drwxr-xr-x   2 plank  staff    64 Feb 27 20:53 dir1
-rw-r--r--   1 plank  staff  1570 Feb 27 20:45 f1.txt
-rw-r--r--   1 plank  staff  1596 Feb 27 20:45 f2.txt
-rw-r--r--   1 plank  staff  1544 Feb 27 20:45 f3.txt
UNIX> ./a.out
f2.txt
aaa.txt
f1.txt
f3.txt
..
.
dir1
UNIX> 
Here are some useful prototypes and the like:

extern JRB make_jrb();   
extern JRB jrb_insert_str(JRB tree, char *key, Jval val);
extern JRB jrb_insert_int(JRB tree, int ikey, Jval val);
extern JRB jrb_insert_dbl(JRB tree, double dkey, Jval val);
extern JRB jrb_insert_gen(JRB tree, Jval key, Jval val, int (*func)(Jval,Jval));
extern JRB jrb_find_str(JRB root, char *key);
extern JRB jrb_find_int(JRB root, int ikey);
extern JRB jrb_find_dbl(JRB root, double dkey);
extern JRB jrb_find_gen(JRB root, Jval, int (*func)(Jval, Jval));
extern void jrb_delete_node(JRB node);  
extern void jrb_free_tree(JRB root);  
#define jrb_traverse(ptr, lst) \
  for(ptr = lst->flink; ptr != lst; ptr = ptr->flink))

extern Dllist new_dllist();
extern dll_append(Dllist, Jval);
extern dll_prepend(Dllist, Jval);
extern dll_insert_b(Dllist, Jval);
extern dll_insert_a(Dllist, Jval);
extern dll_delete_node(Dllist);
extern free_dllist(Dllist);
#define dll_traverse(ptr, lst) \
  for(ptr = lst->flink; ptr != lst; ptr = ptr->flink))

char *strchr(const char *s, int c);           /* Return NULL if not found */
char *strstr(const char *s1, const char *s2); /* Return NULL if not found */

int lstat(const char *path, struct stat *buf);
int stat(const char *path, struct stat *buf);

DIR * opendir(const char *dirname);
int closedir(DIR *dirp);
struct dirent *readdir(DIR *dirp);
/* The only thing you care about in a struct dirent is the field d_name which is a char * */

     struct stat { 
         dev_t    st_dev;    /* device inode resides on */
         ino_t    st_ino;    /* inode's number */
         mode_t   st_mode;   /* inode protection mode */
         nlink_t  st_nlink;  /* number or hard links to the file */
         uid_t    st_uid;    /* user-id of owner */
         gid_t    st_gid;    /* group-id of owner */
         dev_t    st_rdev;   /* device type, for special file inode */
         off_t    st_size;   /* file size, in bytes */
     };

int chmod(const char *path, mode_t mode);
mode_t umask(mode_t cmask);
int system(const char *command);
int open(const char *path, int oflag, ...);


Question 12 - 16 points

You are writing code for an embedded application on a single-board computer running a primitive version of Unix. In particular, it does not support the C stdio library. Your application needs to write 16-byte log records to a file, sometimes in large bursts, and sometimes more sporadically. Your co-worker has given you two files. The first is logfile.h, which defines three procedures that the application will call to open, close and write its log records. It is nice and simple:

void *open_logfile(const char *filename);
void write_logentry(void *logfile, void *logentry);
void close_logfile(void *logfile);

The second file is a quick implementation that your co-worker wrote to show you the desired behavior:

#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include "logfile.h"

typedef struct {
  int fd;
} Logfile;

void *open_logfile(const char *filename)
{
  int fd;
  Logfile *l;

  fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);
  if (fd < 0) return NULL;

  l = (Logfile *) malloc(sizeof(Logfile));
  l->fd = fd;
  return (void *) l;
}

void write_logentry(void *logfile, void *logentry)
{
  Logfile *l;

  l = (Logfile *) logfile;
  write(l->fd, logentry, 16);
}

void close_logfile(void *logfile)
{
  Logfile *l;

  l = (Logfile *) logfile;
  close(l->fd);
  free(l);
}

Your job is rewrite this implementation to make it efficient.

Here are prototypes of system/library calls that you may use. You may not use any others:

int open(const char *path, int oflag, [int mode]);
int write(int fildes, const void *buf, size_t nbytes);
int close(int fildes);
void *malloc(int size);
void free(void *ptr);
void *memcpy(void *dest, const void *src, size_t nbytes);

Please do not bother with #include statements. You don't have to do any additional error checking than the above procedures do.