CSCI 241 - Homework 4: Bits are bits
Due by 11:59.59pm Thursday, March 16
Introduction
In this lab you will get experience dealing with data on the binary level. You will also have to deal with static local variables and using global variables. In addition, you’ll be expected to use conditional compilation via a Makefile, creating header files, etc.
You will work with a partner on this assignment.
It is expected that you work together and equally contribute to the
development of your solution. Also, you are both responsible for understanding
how your solution works. You need only submit one assignment per group, but
clearly indicate your partnership in the README and comments for files. You should play with collaborating on github as you are doing this.
The URL for the github repository for this account is https://classroom.github.com/a/tZP6yuY5
Part 1 - converting data to binary and back
In this part, you will be creating 2 programs. encode_bits which will generate the “binary” representation of a file and decode_bits which will take that representation and convert it back to the original format.
encode_bits
Create a program called encode_bits. This program should use getchar() to read in characters one at a time and then call print_bits() (see below) to output that character as a sequence of ‘1’ and ‘0’ characters. It should stop on EOF.
decode_bits
Create a program called decode_bits. This program should use getchar() to read in characters one at a time and then call decode_bits() (see below) to output that sequence of ‘1’ and ‘0’ characters as actual characters. It should stop on EOF.
bits.c & bits.h
Create a file called bits.c that contains the following two functions and a header file bits.h that contains a guard against multiple inclusion, other needed includes, and function prototypes.
void print_bits(int ch)
Takes the character ch
and outputs its value in binary format
with all leading zeros and with the MSB first. For example, the letter ‘A’
has a value of 0x41, and should be output as 01000001
. A newline
character has a value of 0x0a
and should be output as 00001010
.
You should not assume the number of bits that are in a char, instead use
the constant CHAR_BIT
from limits.h.
void decode_bits(int ch)
If the character is whitespace, skip it. (You might want to use
isspace
() in ctype.h.)
If the character is a ‘1’ or a ‘0’, you should add it into an output buffer (numerically, shifting the current contents appropriately). Once you’ve seen CHAR_BIT bits, you should print the corresponding character out.
If the character is neither white space, nor a binary digit, you should
print a message to the screen, and then use exit()
to quit your
program with a non-zero value.
Programming hints for part 1
You’ll probably need to use static local variables to handle
decode_bits
since it only prints out a character every CHAR_BIT
calls to it.
Don’t forget to make rules in your Makefile include the correct
compilation. bits.o
should be the dependency for the two other
programs, and you should have a separate rule for its compilation.
Part 2 - Number Transformation
For the second part, you will be creating a function to read in a positive
integer value and storing the result in a long
integer variable.
You will also be creating 4 short programs that will use that function to
read in integers and output them in one of 4 different formats – binary,
decimal, octal, or hexadecimal.
reading in a number
Create a file called getnum.c
and another called
getnum.h
. In getnum.c
you will create the function
getnum()
that is used by your other programs.
long getnum(void)
Read in a positive integer in one of 4 formats described below and then
return the value.
Skip leading whitespace and stop reading in a number on the next occurrence of whitespace.
- Binary - begins with a leading
0b
and then a sequence of0
and1
characters - Octal - begins with a leading
0
followed by 1 or more digits from 0-7 - Decimal - either a single
0
or a digit from 1-9 followed by zero or more digits from 0-9 - Hexadecimal - begins with a leading
0x
followed by 1 or more digits from 0-9 or A-F
If the integer read is invalid, somehow signal the caller that the value returned is not valid. I’d suggest you consider a global variable.
If the input is invalid, skip to the next whitespace in the input or EOF.
You might find it useful to “unread” a character.
You can do so using ungetc(ch, stdin);
where “ch” is the character
you just read. Note that you can only un-read one character at a time
until you read in a new character.
Building a state diagram like the one below might be useful in visualizing the behavior of this function. Note: The image includes the case for negative numbers, which are not part of the main assignment.
Printing out a number
You will then create 4 short programs that will read in a sequence of number and then output them one per line in a specified format. All 4 will be using sign-magnitude format. (If negative, print out the sign and then the rest as if it were positive – important for binary.)
tobinary
- outputs the signed value in binary with a leading0b
todecimal
- outputs the signed value in base 10 with no leading0
characters-
tooctal
- outputs the signed value in base 8 with a single leading0
tohex
- outputs the signed value in base 16 (uppercase) with a leading0x
In the event that the integer being read is invalid, simply print
“ERROR
” to the screen.
Loop until no more integers remain.
Programming hints for part 3
-
You will probably want to have more functions than just the one required in this part. You can also have other functions or global flags that can be used by the toBASENAME programs.
-
You may ignore the possibility of an integer overflow in the numbers you read.
-
At no point in time should your program segfault or generate errors when running under valgrind.
-
Be sure your program can handle both upper and lower case in hex. (“man ctype” for inspiration)
-
Be sure to handle the special case of single ‘0’ character.
-
Be sure to handle the case where EOF immediately follows a valid integer.
Don’t forget to add these targets into your Makefile.
The rest of the info
Sample program
I’ve included my sample solution in ~rhoyle/pub/cs241/hw4/
with
binaries that should work on the lab machines.
decode_bits
should exactly undo encode_bits
. For example,
you should get no output from the following:
% ./encode_bits < ./encode_bits | ./decode_bits > output % diff -q encode_bits output
You can also chain together the base transformation if you like
% echo 32767 0xffff 071 0b101 cat | ./tobinary | ./tohex | ./tooctal | ./todecimal
32767
65535
57
5
ERROR
Extra Credit
Read in a flag on the command line that specifies the base for the output. It should be one of:
- -b: binary (the default, as specified above)
- -o: octal (convert all 3-bit strings into a number from 0-7)
- -h: hex (convert all 4-bit strings into a number from 0-F)
Note, your output, if octal, should be the regular number without the leading 0. The number, if hex, should be the regular number without the leading 0x. You will need to modify both encode_bits
and decode_bits
for full credit, as you’ll need to be able to decode each type of encoding.
Handle negative numbers
README
As with the first project, I want you to create a file called README
- Your name (and partner’s name) and the date
- A list of the programs with a short one-line description of each
- A description of how someone should use your version of
getnum()
including a description of how it signals the validity of the value read. - A list of all remaining compilation problems, warnings, or errors. Note that for full marks, it is expected that you will have corrected all of these things.
- A statement about any valgrind errors which occur
- An estimate of the amount of time you spent designing, implementing, and debugging these programs
- The honor code statement:
I have adhered to the Honor Code in this assignment
Submission
Now you should make clean
to get rid of your executables and
commit your folder containing your source files, README, and Makefile through git, as you did in last week’s assignment. For a refresher, refer to those instructions.
Grading
Here is what I am looking for in this assignment:
- A working Makefile with your programs, all, and clean as targets
- All programs should have comments including your name and date at the top. Functions should have a brief description of what they do and an explanation of their return values.
- All programs should compile using
gccx
</tt> on clyde with no compiler warnings or errors. - All programs should run using
valgrind
on clyde with no warnings or errors.
Last Modified: October 07, 2022 - Roberto Hoyle, with material from Ben Kuperman