W
warint
My lecturer gave us an assignment. He has a very "mature" way of
teaching in that he doesn't care whether people show up, whether they
do the assignments, or whether they copy other people's work.
Furthermore, he doesn't even mark the assignments, but rather gives
tips and so forth when going over students' work. To test students'
capabilities for the purpose of state exams and qualifications though,
he actually sits down with us at a computer and watches us write code.
We were issued with an assignment a yesterday. I have attempted the
assignment, and have re-produced the code below. I'd appreciate if
you'd give me advice, suggestions, etc. on the code. Again, I must
stress that you won't simply be "doing my homework for me".
Here's the assignment the lecturer gave us:
[begin quote]
Write a program to check the validity of a serial number found on a
Euro bank note. The program must be fully portable and compliant with
the C89 Standard. The program should exploit the standard library
where possible. The program should also be expected to perform
efficiently, both in terms of resource consumption and execution
speed, on a wide variety of platforms. The program should be well-
commented and easily understood where possible. Debug-mode facilities
such as "assert" should be exploited no matter how much redundancy
they add -- however the efficiency of the release-mode executable
should not be burdened by debug-mode features. The student must find
out for themselves how to check the validity of a Euro bank note
serial number. The program should not malfunction on account of bad
user input. Best of luck.
[end quote]
Before I begin, here's an excerpt from Wikipedia which discusses how
to check the validity of the serial number:
[begin excerpt]
Replace the initial letter by its position in the alphabet (that is L
is 12, M is 13,..., Z is 26).
Add up this number and every digit of the serial number. For example:
U08217383936 is 21 + 0 + 8 + 2 + 1 + 7 + 3 + 8 + 3 + 9 + 3 + 6 = 71
Add up all the digits of this new number, redo as many times as
necessary until you obtain a one-digit number. Alternatively computer
programmers may find the familiar MOD 9 function easier to implement.
This gives the same result.
The resulting number must be 8 - in the example above, 7 + 1 = 8 or 71
MOD 9 = 8, so it's correct.
[end excerpt]
Now here's my code. You're probably gonna kill me for this, but I
don't have a compiler to hand, so, even though I've checked over the
code, I can't say with 100% certainty that it will compile.
#include <assert.h> /* For assert */
#include <ctype.h> /* For stuff like isupper */
#include <stdlib.h> /* For EXIT_FAILURE */
#include <stdio.h> /* For puts and gets */
int const serial_len = 12; /* Serial number = one letter followed
by eleven digits */
/* Function: DigitFromChar
Converts '0' to 0, '5' to 5, '3' to 3, etc..
Exploits C89 feature that '0' through '9' must be consecutive.
Release Mode: UNSAFE because behaviour is undefined if input is
invalid
Debug Mode: SAFE because assertion fails if input is invalid
*/
unsigned DigitFromChar(char const x)
{
assert( x >= '0' && x <= '9' );
return x - '0';
}
/* Function: NumberFromUpperLetter
Converts 'A' to 1, 'B' to 2, 'C' to 3... and so on.
Uses a simple switch statement.
Release Mode: UNSAFE because behaviour is undefined if input is
invalid
Debug Mode: SAFE because assertion fails if input is invalid
*/
unsigned NumberFromUpperLetter(char const x)
{
assert(isupper(x));
switch (x)
{
case 'A': return 1; case 'B': return 2; case 'C': return 3;
case 'D': return 4; case 'E': return 5; case 'F': return 6;
case 'G': return 7; case 'H': return 8; case 'I': return 9;
case 'J': return 10; case 'K': return 11; case 'L': return 12;
case 'M': return 13; case 'N': return 14; case 'O': return 15;
case 'P': return 16; case 'Q': return 17; case 'R': return 18;
case 'S': return 19; case 'T': return 20; case 'U': return 21;
case 'V': return 22; case 'W': return 23; case 'X': return 24;
case 'Y': return 25; case 'Z': return 26;
}
}
/* Function: IsValidEuroSerial
Returns false if serial is invalid, otherwise true.
Loops through the characters, summing with each iteration.
Release Mode: UNSAFE because behaviour is undefined if input is
invalid
Debug Mode: SAFE because assertion fails if input is invalid
*/
int IsValidEuroSerial(char const *p)
{
int const assert_dummy = assert(p);
char const *const pend = p + serial_len;
unsigned sum = NumberFromUpperLetter(*p++);
do
{
assert(isnum(*p));
sum += DigitFromChar(*p++);
} while (pend != p);
return 8 == sum%9;
}
int main(void)
{
char input[serial_len + 1],
*p = input + 1,
const *const pnull = input + (sizeof input / sizeof *input -
1);
puts("Enter Euro banknote serial number: ");
gets(input);
if (!isupper(*input)) goto Bad; /* Check first char is upper
letter */
do if (!isnum(*p++)) goto Bad; /* Check each char is a digit */
while (pnull != p);
puts(IsValidEuroSerial(input) ? "\n\nValid\n" : "\n\nINVALID\n");
return 0;
Bad:
puts("\n\nInvalid Input. Input must consist of an uppercase
letter followed by eleven digits only.\n");
return EXIT_FAILURE;
}
The shortcomings I can see so far are:
1) The char array is looped through twice, once in main and once in
"IsValidEuroSerial". I should consider writing an
"IsValidEuroSerial_Safe" function so that it can be reduced to one
loop.
2) I'm sure you will all jump down my throat about "gets", but please
just give suitable alternatives.
Thanks a lot for your time,
Martin
teaching in that he doesn't care whether people show up, whether they
do the assignments, or whether they copy other people's work.
Furthermore, he doesn't even mark the assignments, but rather gives
tips and so forth when going over students' work. To test students'
capabilities for the purpose of state exams and qualifications though,
he actually sits down with us at a computer and watches us write code.
We were issued with an assignment a yesterday. I have attempted the
assignment, and have re-produced the code below. I'd appreciate if
you'd give me advice, suggestions, etc. on the code. Again, I must
stress that you won't simply be "doing my homework for me".
Here's the assignment the lecturer gave us:
[begin quote]
Write a program to check the validity of a serial number found on a
Euro bank note. The program must be fully portable and compliant with
the C89 Standard. The program should exploit the standard library
where possible. The program should also be expected to perform
efficiently, both in terms of resource consumption and execution
speed, on a wide variety of platforms. The program should be well-
commented and easily understood where possible. Debug-mode facilities
such as "assert" should be exploited no matter how much redundancy
they add -- however the efficiency of the release-mode executable
should not be burdened by debug-mode features. The student must find
out for themselves how to check the validity of a Euro bank note
serial number. The program should not malfunction on account of bad
user input. Best of luck.
[end quote]
Before I begin, here's an excerpt from Wikipedia which discusses how
to check the validity of the serial number:
[begin excerpt]
Replace the initial letter by its position in the alphabet (that is L
is 12, M is 13,..., Z is 26).
Add up this number and every digit of the serial number. For example:
U08217383936 is 21 + 0 + 8 + 2 + 1 + 7 + 3 + 8 + 3 + 9 + 3 + 6 = 71
Add up all the digits of this new number, redo as many times as
necessary until you obtain a one-digit number. Alternatively computer
programmers may find the familiar MOD 9 function easier to implement.
This gives the same result.
The resulting number must be 8 - in the example above, 7 + 1 = 8 or 71
MOD 9 = 8, so it's correct.
[end excerpt]
Now here's my code. You're probably gonna kill me for this, but I
don't have a compiler to hand, so, even though I've checked over the
code, I can't say with 100% certainty that it will compile.
#include <assert.h> /* For assert */
#include <ctype.h> /* For stuff like isupper */
#include <stdlib.h> /* For EXIT_FAILURE */
#include <stdio.h> /* For puts and gets */
int const serial_len = 12; /* Serial number = one letter followed
by eleven digits */
/* Function: DigitFromChar
Converts '0' to 0, '5' to 5, '3' to 3, etc..
Exploits C89 feature that '0' through '9' must be consecutive.
Release Mode: UNSAFE because behaviour is undefined if input is
invalid
Debug Mode: SAFE because assertion fails if input is invalid
*/
unsigned DigitFromChar(char const x)
{
assert( x >= '0' && x <= '9' );
return x - '0';
}
/* Function: NumberFromUpperLetter
Converts 'A' to 1, 'B' to 2, 'C' to 3... and so on.
Uses a simple switch statement.
Release Mode: UNSAFE because behaviour is undefined if input is
invalid
Debug Mode: SAFE because assertion fails if input is invalid
*/
unsigned NumberFromUpperLetter(char const x)
{
assert(isupper(x));
switch (x)
{
case 'A': return 1; case 'B': return 2; case 'C': return 3;
case 'D': return 4; case 'E': return 5; case 'F': return 6;
case 'G': return 7; case 'H': return 8; case 'I': return 9;
case 'J': return 10; case 'K': return 11; case 'L': return 12;
case 'M': return 13; case 'N': return 14; case 'O': return 15;
case 'P': return 16; case 'Q': return 17; case 'R': return 18;
case 'S': return 19; case 'T': return 20; case 'U': return 21;
case 'V': return 22; case 'W': return 23; case 'X': return 24;
case 'Y': return 25; case 'Z': return 26;
}
}
/* Function: IsValidEuroSerial
Returns false if serial is invalid, otherwise true.
Loops through the characters, summing with each iteration.
Release Mode: UNSAFE because behaviour is undefined if input is
invalid
Debug Mode: SAFE because assertion fails if input is invalid
*/
int IsValidEuroSerial(char const *p)
{
int const assert_dummy = assert(p);
char const *const pend = p + serial_len;
unsigned sum = NumberFromUpperLetter(*p++);
do
{
assert(isnum(*p));
sum += DigitFromChar(*p++);
} while (pend != p);
return 8 == sum%9;
}
int main(void)
{
char input[serial_len + 1],
*p = input + 1,
const *const pnull = input + (sizeof input / sizeof *input -
1);
puts("Enter Euro banknote serial number: ");
gets(input);
if (!isupper(*input)) goto Bad; /* Check first char is upper
letter */
do if (!isnum(*p++)) goto Bad; /* Check each char is a digit */
while (pnull != p);
puts(IsValidEuroSerial(input) ? "\n\nValid\n" : "\n\nINVALID\n");
return 0;
Bad:
puts("\n\nInvalid Input. Input must consist of an uppercase
letter followed by eleven digits only.\n");
return EXIT_FAILURE;
}
The shortcomings I can see so far are:
1) The char array is looped through twice, once in main and once in
"IsValidEuroSerial". I should consider writing an
"IsValidEuroSerial_Safe" function so that it can be reduced to one
loop.
2) I'm sure you will all jump down my throat about "gets", but please
just give suitable alternatives.
Thanks a lot for your time,
Martin