CSE 333 21wi Exercise 2

out: Monday, January 11, 2021
due: Wednesday, January 13, 2021 by 10:00 am.

Setup

If your working copy of your cse333 repository doens't include an ex02 directory, do a git pull to fetch it. Ignore the advanced/ directory. It is explained at the very end and is completely optional.

Problem Description

We've distributed two files. (1) dumphex.h declares a single function:

int DumpHex(void* mem_addr, uint32_t num_bytes);
(2) ex02.c contains a main() that calls DumpHex(). No changes are needed to either file.

No definition of DumpHex() is provided. Your task is to implement it. Your code should go in a new file, dumphex.c. (Remember to git add that file so that it makes its way into your repository.)

DumpHex() prints (to stdout) the value of each byte of memory starting with *mem_addr and going for num_bytes bytes. Each byte's value is printed in hex and consecutive byte values are separated by a space. The printed string of bytes is preceded by a string indicating what the arguments were. Your output should mimic this format:

The 4 bytes starting at 0x7fff1081856c are: ff 01 30 4e

DumpHex() returns 0 for success and a distinct positive value for each distinct kind of error it detects. (Once again, it is up to you to identify what the errors are and what codes are returned.)

Possible C Challenges

Your implementation of DumpHex will need to do a few things:

Unnatural Debug Aide

It's easier to debug when you know correct behavior is, so we're telling you. Your code should produce output that is identical to the following, except that there are portions of the output that are printing uninitialized values and the address given has undergone some randomization. That means those things might change from run to run. (Which part is uninitialized? That's something for you to figure out.)

$bash gcc -Wall -std=c17 -g -o ex02 ex02.c dumphex.c
$bash ls
ex02 ex02.c
$bash ./ex02
The 1 bytes starting at 0x7fff3c84a1ff are: 30
The 4 bytes starting at 0x7fff3c84a1f4 are: 01 00 00 00
The 4 bytes starting at 0x7fff3c84a1f8 are: 00 00 80 3f
The 8 bytes starting at 0x7fff3c84a1e8 are: 00 00 00 00 00 00 f0 3f
The 24 bytes starting at 0x7fff3c84a1d0 are: 30 b0 f0 00 01 00 00 00 00 00 80 3f 00 00 00 00 00 00 00 00 00 00 f0 3f

Note that the number of bytes shown on the last line is not the sum of the numbers shown on the previous lines. That's related to what part of the data is uninitialized.

Requirements

You must not alter main() or dumphex.h. If you notice any style issues with them, ignore them and focus on getting your added code right.

Your code must:

Turn-In

You should tag your submission ex02-final and submit by pushing to your repository.

OPTIONAL

The advanced/ subdirectory contains a more sophisticated implementation of the mainline. It uses an advanced preprocessor feature to make the code easier to create and easier to maintain. What it does IS NOT A KEY SKILL.

WHEN YOU FINISH the assignment, then IF YOU WANT TO, have a look at the advanced implementation. There are some comments in a README file to help explain it to you. First try to understand the mechanics -- what is happening? Next consider the claims about why this is better that are made in the README and decide if you agree or not. Finally, try to state convincingly why the preprocessor macro TEST(x) could NOT be implemented as a C subroutine.

The advanced/ material is intended to be fun. If you don't find it fun, stop!

P.S. You should feel very free to ask questions of the staff about what is going on, but only if you're actually enjoying this extra material.

Additional Optional Note

The specification of DumpHex is arguably terrible. It asks you to do two things: convert the contents of memory to a hex string and print that string. A function should do only one thing. That way it is more easily used in ways you may not have anticipated when first writing it.

In Java, we'd fix this by having a routine that returned a string and that's it, without printing it. If the calling routine wanted to print, it could. If it wanted to remember the string in some database, it could. If it wanted to compare the contents of two different pieces of memory (as strings), it could. We tend not to do that in C because strings are a real pain, primarily because C doesn't have them (and doesn't do garbage collection). They're not so much of a pain in C++, though, so we'd maybe do things differently using that language.

The general lessons here are (1) that good design often involves composing a set of functions each of which does just one thing, and so when you design a function that does more than one thing it should give you pause, and (2) it's pretty close to never a good idea for a subroutine to print anything unless that's the only thing it does.