Simple linked list exercise.
- Each link in the list is a bignum digit. In our case our “bigit” is just a value in 0–9.
- A number has sign, plus the singly-linked list of digits from least- to most-significant.
- Don’t forget to clean up (
free()
) anything you allocate (with malloc()
).
- Reading from file is just:
- skipping whitespace
- checking for an optional sign
- reading digits until a non-digit is encountered
- Notice how we are careful to put back characters that don’t match what we are looking for?
- For printing the digits we just use a recursive function. If you expect your bignums to be really big, don’t do this. There are all kinds of ways to overcome this limitation if it matters. For a homework it probably doesn’t.
- Always pass a
number
by pointer. More on this below.
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
//-----------------------------------------------------------------------------
// Bignum digit type is an integer value in 0..9
struct digit
{
int digit;
struct digit * next;
};
// Prepend a digit to a list of digits
struct digit * make_digit( int _digit, struct digit * _next )
{
struct digit * d = malloc( sizeof(struct digit) );
if (d)
{
d->digit = _digit;
d->next = _next;
}
return d;
}
void free_digits( struct digit * digit )
{
while (digit)
{
struct digit * next = digit->next;
free( digit );
digit = next;
}
}
//-----------------------------------------------------------------------------
// A bignum is:
// • a sign: -1 or 1
// • a list of digits starting with the least-significant
struct number
{
int sign;
struct digit * digits;
};
typedef struct number number;
void free_number( number * n )
{
free_digits( n->digits );
}
//-----------------------------------------------------------------------------
// Read a bignum from FILE *
void skip_whitespace( FILE * f )
{
int c;
while (isspace( (unsigned char)(c = fgetc( f )) )) { }
if (c != EOF) ungetc( c, f );
}
number read_number( FILE * f )
{
number n = { 1, NULL };
skip_whitespace( f );
int c = fgetc( f );
switch (c)
{
case '-': n.sign = -1; c = fgetc( f ); break;
case '+': n.sign = 1; c = fgetc( f ); break;
}
while (isdigit( (unsigned char)c ))
{
n.digits = make_digit( c-'0', n.digits );
c = fgetc( f );
}
if (c != EOF) ungetc( c, f );
return n;
}
//-----------------------------------------------------------------------------
// Print a bignum to FILE *
void print_digits( FILE * f, struct digit * d )
{
if (!d) return;
print_digits( f, d->next );
fprintf( f, "%c", d->digit+'0' );
}
void print_number( FILE * f, number * n, const char * s )
{
if (!n->digits) return;
if (n->sign < 0) fprintf( f, "-" );
print_digits( f, n->digits );
fprintf( f, "%s", s );
}
//-----------------------------------------------------------------------------
int main(void)
{
number n1 = read_number( stdin );
number n2 = read_number( stdin );
print_number( stdout, &n1, "\n" );
print_number( stdout, &n2, "\n" );
free_number( &n1 );
free_number( &n2 );
return 0;
}
As already noted, and exampled, a number
should be passed by pointer to functions that will modify the number. Since it doesn’t make any sense to manage const
-ness with a number, you might as well make all functions that take number
s take a pointer so to be as un-confusing as possible.
For example, a function that sums two number
s and returns a new number
might look like:
number add( number * a, number * b ); // result = a + b
A function that increments a number might look like:
void inc( number * n, number * by ); // n += by
Just remember: every time you get a new number
, you must free it before terminating the current scope, just as the example in main()
.