Registered User schrieb:
I'm a newbie and have attempted to create a program to convert a string
containing the representation of a number in a specified base to a
decimal integer. Here's the code:
#include <stdio.h>
unsigned todecimal( char *s, int base)
"to" followed by a lower case letter belongs as an identifier
to the implementation; i.e. this name might at some time clash
with something the standard library provides.
You do not modify the passed string, so you may as well pass a
const char *.
Something to document early: You check neither s!=NULL nor
2<=base<=36. If the user does not find this, you may get
invalid input and the ensuing crash or returned garbage is
somehow _your_ fault...
{
unsigned num = 0, mul;
Note: I'd rather use unsigned long which is the widest unsigned
integer type in C89 (or unsigned long long for C99).
int len=0;
while (*++s != '\0')
If s is "", then you run off wildly through storage that does
not necessarily belong to you. In addition, you modify a
parameter -- it may be a better idea to create a copy and modify
that.
Either stick with strlen() or amend this to
const char *tmp;
for (tmp = s; *tmp != '\0'; ++tmp) {
++len;
}
++len;
--s;
for (mul=1; len>=0; --len, mul*=base, --s)
{
if (*s >= '0' && *s <= '9')
num+= (*s-'0')*mul;
else if (*s>='A'&& *s<='Z')
num+= (*s-'A'+10)*mul;
else if (*s>='a' && *s<='z')
num+=(*s-'a'+10)*mul;
This breaks if 'Z'-'A' != 25...
A better method is using a digits array:
char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
and set the allowed range:
digits[base] = '\0';
and then find the digit using strchr()
char *occurred = strchr(digits, tolower(*s));
if (occurred && *occurred) {
int digit = occurred - s;
....
The other thing is that you do not make sure that you have
no overflow: For num of type unsigned int, this means:
unsigned int add;
if (UINT_MAX / digit < mul) {
/* mul will become too large in the next step and is
* already too large for digit*mul to be representable */
/* Your error handling here */
}
add = digit * mul;
if (UINT_MAX - add < num) {
/* mul will become too large in the next step and is
* already too large for num+digit*mul to be representable */
/* Your error handling here */
}
}
return num;
}
You omitted error handling; there are several types of errors, so
you should decide how you want to flag "everything okay" vs. "something
bad happened".
Bad things that could happen:
s == NULL
base < 2
base strlen(digits)
one of the characters not in digits (where digits[base] == 0)
overflow
Should everything be flagged differently?
How do you want to flag this?
As you have unsigned int values, the obvious candidate for an error
return is UINT_MAX. Alternatively, you can have a look at the
strtoul() function and how it flags "bad" values.
int main()
int main (void)
-- we have that much time.
{
char s[33];
int base;
printf("Enter the number and its base: ");
scanf("%s%d",s, &base);
Always make sure that you did get what you expected when using
*scanf():
if (2 != scanf("%s%d", s, &base)) {
/* Your error handling here */
}
printf("Decimal value = %u\n", todecimal(s, base) );
return 0;
}
Note: As you have an excellent alternate version (strtoul()), you
can test your function in comparison with strtoul(). Maybe you have
to wrap strtoul() to get the same outward behaviour.
When testing, do not forget corner cases such as:
s = NULL, s = ""
base = -1, base = 0, base = 1, base = 2, base = 36, base = 37
s = "/", s = "-1", s = "3" for base = 2, s = "0",
s = string holding UINT_MAX, s = string holding UINT_MAX + 1, ....
Cheers
--
E-Mail: Mine is an /at/ gmx /dot/ de address.