Error passing by reference when making os - c

I am making an os and have booted into a 64 bit kernel made in c. I have made a print function which is working and am trying to make a function to convert hex values to string so I can print them. My code is causing boot loops, yet when I compile the exact same code to run normally in linux it works perfectly. The relevant code:
int logarithm(double value, int base, int* output) {
int i = 0;
while (value > 1) {
value /= base;
i++;
}
*output = i;
}
int power(int value, int power, int* output) {
if (power == 0) {
value = 1;
} else {
for (int i = 0; i < (power - 1); i++) {
value *= value;
}
}
*output = value;
}
void hexToStr(unsigned int hex, char** string) {
int hexLength = 0;
logarithm((double)hex, 16, &hexLength);
char output[hexLength];
output[hexLength] = 0;
int powerValue = 0;
for (int i = 0; i < hexLength; i++) {
power(16, i, &powerValue);
output[hexLength - i - 1] = (hex & (powerValue * 15)) / powerValue + '0';
}
*string = output;
}
If I change the hexToStr() function code to this (removing the need for logarithm() and power() functions by hardcoding values for the string), it works in both linux and my kernel:
void hexToStr(unsigned int hex, char** string) {
int hexLength = 10;
char output[hexLength];
output[hexLength] = 0;
int powerValue = 0;
for (int i = 0; i < hexLength; i++) {
output[hexLength - i - 1] = 'A';
}
*string = output;
}
Any suggestions as to why this would happen?

The presented code invokes undefined behavior. For example let's consider this function
void hexToStr(unsigned int hex, char** string) {
int hexLength = 10;
char output[hexLength];
output[hexLength] = 0;
int powerValue = 0;
for (int i = 0; i < hexLength; i++) {
output[hexLength - i - 1] = 'A';
}
*string = output;
}
In this assignment statement:
output[hexLength] = 0;
there is written data outside the array because the valid range of indices is [0, hexLength).
Or the function sets a pointer passed to the function by reference to the local array output that will not be alive after exiting the function. So the returned pointer will have an invalid value.
Another example the result value of the function power when the parameter value is equal to 3 and the parameter power is equal to 3 will be equal to 81 instead of 27 due to the assignment statement in this for loop.
for (int i = 0; i < (power - 1); i++) {
value *= value;
}
Moreover the function returns nothing though its return type is not void.
int power(int value, int power, int* output) {
Also this expression
(hex & (powerValue * 15)) / powerValue + '0'
does not make a sense.

Needed to enable SSE unit to work with floats and doubles. As well as change how values are passed back. Working code:
void log(float value, float base, uint64_t* output) {
uint64_t i = 0;
while (value >= 1) {
value /= base;
i++;
}
*output = i;
}
void pow(uint64_t value, uint64_t exponent, uint64_t* output) {
uint64_t result = 1;
for (uint64_t i = 0; i < exponent; i++) {
result = result * value;
}
*output = result;
}
void hexToStr(uint64_t hex, char* output) {
uint8_t hexLen = 16;
log((float)hex, (float)16, &hexLen);
char result[hexLen + 3];
result[0] = '0';
result[1] = 'x';
result[hexLen + 2] = 0;
uint64_t powerValue = 1;
for (uint8_t i = 0; i < hexLen; i++) {
pow(16, i, &powerValue);
result[hexLen - i + 1] = (hex & (uint64_t)(powerValue * (uint64_t)15)) / powerValue + '0';
}
for (uint8_t i = 0; i < hexLen + 3; i++) {
switch(result[i]) {
case ':':
result[i] = 'A';
break;
case ';':
result[i] = 'B';
break;
case '<':
result[i] = 'C';
break;
case '=':
result[i] = 'D';
break;
case '>':
result[i] = 'E';
break;
case '?':
result[i] = 'F';
break;
}
output[i] = result[i];
}
}

Related

Is binary to decimal conversion rounded? how?

Got to transform a binary number to decimal for recoding printf (no lib or functions allowed except malloc and write). i'm doing my calculs on char *, so it can't overflow. But when i hit a certain size, my result differ from a online binary converter, and i noticed that the binary converter keep always only 20 digits.
for exemple :
binary : 1.11111111111111111111111111
binary converter = 1.99999998509883880615,
my o converter == 1.99799896499882880605234375,
I guess the online converter keep the result rounded in an unsigned long long, but i don't understand how this rounding is calculated.
Do you have any clues?
Here is my code:
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
char *ft_binary_pow(char *tmp, int i)
{
int j;
int div;
int remnant;
j = 0;
remnant = 0;
tmp[0] = '1';
tmp.sign = 0;
while (i > 0)
{
while (isdigit(tmp[j]) || remnant != 0)
{
if (tmp[j])
div = ((tmp[j] - '0') * 10) / 2;
else
div = 0;
tmp[j] = ((div / 10) + remnant) + '0';
remnant = div % 10;
j++;
}
j = 0;
i--;
}
return (tmp);
}
char *ft_add_tmp(char *ret, char *tmp)
{
int i;
int j;
int add;
int remnant;
i = strlen(tmp);
add = 0;
remnant = 0;
while (--i >= 0)
{
if (!ret[i])
add = tmp[i] - '0';
else
add = (ret[i] - '0') + (tmp[i] - '0');
if ((add % 10 + remnant) < 10)
ret[i] = (add % 10 + remnant) + '0';
else
ret[i] = '0';
remnant = add / 10 ? 1 : 0;
}
return (ret);
}
int main(void)
{
int i;
char *lol = "11111111111111111111111111";
char *ret;
char *tmp;
if (!(ret = (char *)calloc(340, sizeof(char))))
return (0);
i = 0;
ret[0] = '1';
while (lol[i])
{
if (lol[i] == '1')
{
if (!(tmp = (char *)calloc(50, sizeof(char))))
return (0);
tmp = ft_binary_pow(tmp, i + 1);
ret = ft_add_tmp(ret, tmp);
free(tmp);
}
i++;
}
printf("ret = %s\n", ret);
return (0);
}
Edited for more readable code
Thanks for your time!
Code fails even with lol = "111111111".
Code fails in ft_add_tmp() to handle a carry (overflow) into the next most significant digit.
else {
ret.decimal[i] = '0';
// add something like this and then also prorogate carry as needed to higher digits
ret.decimal[i-1]++;
}
Suggested correction and simplification:
char* ft_add_tmp(char *ret, const char *tmp) {
size_t i = strlen(tmp);
unsigned carry = 0;
while (i-- > 0) {
unsigned sum = ret[i] ? ret[i] - '0' : 0;
sum += tmp[i] - '0' + carry;
ret[i] = sum % 10 + '0';
carry = sum / 10;
}
assert(carry == 0);
return (ret);
}

Realloc Crashing on Passed Pointer

I can't seem to find a question that matches exactly what I'm doing, so here goes. Below is a cut down version of my C app down to where a problem lies. I know it's ugly code and missing a few error checks but it was just for me to figure out this problem. As it stand the sample below should convert all 'A's to 'BCDE's. The comments in the code describe the issue. (runMe is executed first)
int runMe2(char *in, char **buffer) {
long x;
long b_size = 0;
long i_size = 1000;
long i = 0;
char t_buffer[1006];
// Initial malloc small as it will grow
*buffer = (char *)malloc(2*sizeof(char));
strcpy(*buffer, "");
for (x = 0; x < 999; x++)
t_buffer[x] = 0;
for (x = 0; x < strlen(in); x++) {
if (i >= i_size) {
char *r_buffer;
b_size = b_size + 1006*sizeof(char);
i_size = 0;
// Here is where the problem is.
// The first time through, i=1000, b_size=1006 and everything is fine
// The second time throgh, i=1004, b_size=2012 and the heap crashes on the realloc
r_buffer = (char *)realloc(*buffer, b_size);
if (r_buffer == NULL)
exit(0);
*buffer = r_buffer;
strcat(*buffer, t_buffer);
for (x = 0; x < 999; x++)
t_buffer[x] = 0;
}
if (in[x] == 'A') {
t_buffer[i++] = 'B';
t_buffer[i++] = 'C';
t_buffer[i++] = 'D';
t_buffer[i++] = 'E';
}
}
}
int runMe() {
char *out;
char in[30000];
int x = 0;
// Set up a 29,999 character string
for (x = 0; x < 30000; x++)
in[x] = 'A';
in[29999] = 0;
// Send it as pointer so we can do other things here
runMe2(in, &out);
// Eventually, other things will happen here
free(out);
}
if (i >= i_size) {
...
i_size = 0;
...
}
if (in[x] == 'A') {
t_buffer[i++] = 'B';
...
This can't be right. You'll be writing past the end of t_buffer if in is ever longer than the original i_size. You probably meant to reset i there, not i_size.
Then you're using string functions with t_buffer when you've not guaranteed that it is properly null-terminated - you initialize the first thousand values, but overwrite those in your loop. If you're going to use strcat and friends, you need to take more care to make sure it stays null-terminated. But using memcpy would lead to simpler code since you know the lengths of the arrays involved.
for (x = 0; x < strlen(in); x++) {
...
for (x = 0; x < 999; x++)
...
t_buffer[x] = 0;
This can't be right either, as spotted by Useless. Use a second variable for that, or better use memset.
Just for fun here's a different algo that works and is much simpler than yours:
int runMe2(char *in, char **buffer)
{
// Count number of A's
int number_of_As = 0;
int i, j;
for (i = 0; 0 != in[i]; i++) {
if (in[i] == 'A') {
number_of_As += 1;
}
}
// If number_of_As == 0, you can just do a strdup here and return
// Because of 1st loop, i == strlen(in), no need to call strlen
long bytes = (i - number_of_As + (number_of_As * 4) + 1) * sizeof(char);
// Error check here
// Only 1 memeory allocation needed
*buffer = (char *)malloc(bytes);
// Simple copy loop
for (i = 0, j = 0; 0 != in[i]; i++) {
// If it's an A replace
if (in[i] == 'A') {
(*buffer)[j++] = 'B';
(*buffer)[j++] = 'C';
(*buffer)[j++] = 'D';
(*buffer)[j++] = 'E';
}
// Not an A, just copy
else {
(*buffer)[j++] = in[i];
}
}
// Null terminate
(*buffer)[j] = 0;
return j;
}

C Code for Hexadecimal without using standard libraries

I want to write C Code which converts first string into integer then integer into hexadecimal.
ex: I have Ip iddress as "172.24.18.240" now first find out first dot from it and take the number before it that is "172" convert it into integer then convert it inti hexadecimal and it should do the same for all like 24,18,240 and convert into long/integer value
any help is appreciated.
#include <stdio.h> // testing
int main(int argc, char** argv) // testing
{
char* ipString = argc > 1? argv[1] : "172.24.18.240"; // testing
char* ip = ipString;
unsigned int hex;
for( int i = 0; i < 4; i++ ){
unsigned int n = 0;
for( char c; (c = *ip) >= '0' && c <= '9'; ip++ )
n = 10 * n + c - '0';
hex = (hex << 8) + n;
if( *ip == '.' ) ip++;
}
printf("%08X\n", hex); // testing
return 0; // testing
}
Maybe something like this?
char sn[4];
char *nid = hexString;
int nip[4];
int xnip[4];
int j = 0;
while (*nid != '\0') {
int i = 0;
memset(sn, '\0', sizeof sn);
while (isdigit(*nid)) {
sn[i++] = *nid++;
}
if (*nid == '.')
nid++;
// now sn should be the number part
nip[j] = your_str_to_int(sn);
xnip[j] = your_int_to_hex(nip[j]);
j++;
}
int main(void)
{
char hexChars[] = "0123456789ABCDEF";
char ipString[] = "172.24.18.254";
char hexString[9] = "";
const char* pch = ipString;
int num = 0;
int i = 0;
do
{
if (*pch != '.' && *pch != '\0')
{
num *= 10;
num += (*pch - '0');
}
else
{
hexString[i++] = hexChars[num / 16];
hexString[i++] = hexChars[num % 16];
num = 0;
}
} while (*pch++);
return 0;
}
The hex values will stored in hexString.
int i = 0, sum = 0;
char ipString[] = "172.24.18.240";
do
{
if (isdigit(ipString[i])) sum = sum * 10 + ipString[i] - '0';
else
{ putchar("0123456789ABCDEF"[sum / 16]);
putchar("0123456789ABCDEF"[sum % 16]);
putchar('.');
sum = 0;
}
}
while (ipString[i++] != '\0');
More or less ugly, but should work on IP addresses.

Convert integer to string without access to libraries

I recently read a sample job interview question:
Write a function to convert an integer
to a string. Assume you do not have
access to library functions i.e.,
itoa(), etc...
How would you go about this?
fast stab at it: (edited to handle negative numbers)
int n = INT_MIN;
char buffer[50];
int i = 0;
bool isNeg = n<0;
unsigned int n1 = isNeg ? -n : n;
while(n1!=0)
{
buffer[i++] = n1%10+'0';
n1=n1/10;
}
if(isNeg)
buffer[i++] = '-';
buffer[i] = '\0';
for(int t = 0; t < i/2; t++)
{
buffer[t] ^= buffer[i-t-1];
buffer[i-t-1] ^= buffer[t];
buffer[t] ^= buffer[i-t-1];
}
if(n == 0)
{
buffer[0] = '0';
buffer[1] = '\0';
}
printf(buffer);
A look on the web for itoa implementation will give you good examples. Here is one, avoiding to reverse the string at the end. It relies on a static buffer, so take care if you reuse it for different values.
char* itoa(int val, int base){
static char buf[32] = {0};
int i = 30;
for(; val && i ; --i, val /= base)
buf[i] = "0123456789abcdef"[val % base];
return &buf[i+1];
}
The algorithm is easy to see in English.
Given an integer, e.g. 123
divide by 10 => 123/10. Yielding, result = 12 and remainder = 3
add 30h to 3 and push on stack (adding 30h will convert 3 to ASCII representation)
repeat step 1 until result < 10
add 30h to result and store on stack
the stack contains the number in order of | 1 | 2 | 3 | ...
I would keep in mind that all of the digit characters are in increasing order within the ASCII character set and do not have other characters between them.
I would also use the / and the% operators repeatedly.
How I would go about getting the memory for the string would depend on information you have not given.
Assuming it is in decimal, then like this:
int num = ...;
char res[MaxDigitCount];
int len = 0;
for(; num > 0; ++len)
{
res[len] = num%10+'0';
num/=10;
}
res[len] = 0; //null-terminating
//now we need to reverse res
for(int i = 0; i < len/2; ++i)
{
char c = res[i]; res[i] = res[len-i-1]; res[len-i-1] = c;
}
An implementation of itoa() function seems like an easy task but actually you have to take care of many aspects that are related on your exact needs. I guess that in the interview you are expected to give some details about your way to the solution rather than copying a solution that can be found in Google (http://en.wikipedia.org/wiki/Itoa)
Here are some questions you may want to ask yourself or the interviewer:
Where should the string be located (malloced? passed by the user? static variable?)
Should I support signed numbers?
Should i support floating point?
Should I support other bases rather then 10?
Do we need any input checking?
Is the output string limited in legth?
And so on.
Convert integer to string without access to libraries
Convert the least significant digit to a character first and then proceed to more significant digits.
Normally I'd shift the resulting string into place, yet recursion allows skipping that step with some tight code.
Using neg_a in myitoa_helper() avoids undefined behavior with INT_MIN.
// Return character one past end of character digits.
static char *myitoa_helper(char *dest, int neg_a) {
if (neg_a <= -10) {
dest = myitoa_helper(dest, neg_a / 10);
}
*dest = (char) ('0' - neg_a % 10);
return dest + 1;
}
char *myitoa(char *dest, int a) {
if (a >= 0) {
*myitoa_helper(dest, -a) = '\0';
} else {
*dest = '-';
*myitoa_helper(dest + 1, a) = '\0';
}
return dest;
}
void myitoa_test(int a) {
char s[100];
memset(s, 'x', sizeof s);
printf("%11d <%s>\n", a, myitoa(s, a));
}
Test code & output
#include "limits.h"
#include "stdio.h"
int main(void) {
const int a[] = {INT_MIN, INT_MIN + 1, -42, -1, 0, 1, 2, 9, 10, 99, 100,
INT_MAX - 1, INT_MAX};
for (unsigned i = 0; i < sizeof a / sizeof a[0]; i++) {
myitoa_test(a[i]);
}
return 0;
}
-2147483648 <-2147483648>
-2147483647 <-2147483647>
-42 <-42>
-1 <-1>
0 <0>
1 <1>
2 <2>
9 <9>
10 <10>
99 <99>
100 <100>
2147483646 <2147483646>
2147483647 <2147483647>
The faster the better?
unsigned countDigits(long long x)
{
int i = 1;
while ((x /= 10) && ++i);
return i;
}
unsigned getNumDigits(long long x)
{
x < 0 ? x = -x : 0;
return
x < 10 ? 1 :
x < 100 ? 2 :
x < 1000 ? 3 :
x < 10000 ? 4 :
x < 100000 ? 5 :
x < 1000000 ? 6 :
x < 10000000 ? 7 :
x < 100000000 ? 8 :
x < 1000000000 ? 9 :
x < 10000000000 ? 10 : countDigits(x);
}
#define tochar(x) '0' + x
void tostr(char* dest, long long x)
{
unsigned i = getNumDigits(x);
char negative = x < 0;
if (negative && (*dest = '-') & (x = -x) & i++);
*(dest + i) = 0;
while ((i > negative) && (*(dest + (--i)) = tochar(((x) % 10))) | (x /= 10));
}
If you want to debug, You can split the conditions (instructions) into
lines of code inside the while scopes {}.
I came across this question so I decided to drop by the code I usually use for this:
char *SignedIntToStr(char *Dest, signed int Number, register unsigned char Base) {
if (Base < 2 || Base > 36) {
return (char *)0;
}
register unsigned char Digits = 1;
register unsigned int CurrentPlaceValue = 1;
for (register unsigned int T = Number/Base; T; T /= Base) {
CurrentPlaceValue *= Base;
Digits++;
}
if (!Dest) {
Dest = malloc(Digits+(Number < 0)+1);
}
char *const RDest = Dest;
if (Number < 0) {
Number = -Number;
*Dest = '-';
Dest++;
}
for (register unsigned char i = 0; i < Digits; i++) {
register unsigned char Digit = (Number/CurrentPlaceValue);
Dest[i] = (Digit < 10? '0' : 87)+Digit;
Number %= CurrentPlaceValue;
CurrentPlaceValue /= Base;
}
Dest[Digits] = '\0';
return RDest;
}
#include <stdio.h>
int main(int argc, char *argv[]) {
char String[32];
puts(SignedIntToStr(String, -100, 16));
return 0;
}
This will automatically allocate memory if NULL is passed into Dest. Otherwise it will write to Dest.
Here's a simple approach, but I suspect if you turn this in as-is without understanding and paraphrasing it, your teacher will know you just copied off the net:
char *pru(unsigned x, char *eob)
{
do { *--eob = x%10; } while (x/=10);
return eob;
}
char *pri(int x, char *eob)
{
eob = fmtu(x<0?-x:x, eob);
if (x<0) *--eob='-';
return eob;
}
Various improvements are possible, especially if you want to efficiently support larger-than-word integer sizes up to intmax_t. I'll leave it to you to figure out the way these functions are intended to be called.
Slightly longer than the solution:
static char*
itoa(int n, char s[])
{
int i, sign;
if ((sign = n) < 0)
n = -n;
i = 0;
do
{
s[i++] = n % 10 + '0';
} while ((n /= 10) > 0);
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
return s;
}
Reverse:
int strlen(const char* str)
{
int i = 0;
while (str != '\0')
{
i++;
str++;
}
return i;
}
static void
reverse(char s[])
{
int i, j;
char c;
for (i = 0, j = strlen(s)-1; i<j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
And although the decision davolno long here are some useful features for beginners. I hope you will be helpful.
This is the shortest function I can think of that:
Correctly handles all signed 32-bit integers including 0, MIN_INT32, MAX_INT32.
Returns a value that can be printed immediatelly, e.g.: printf("%s\n", GetDigits(-123))
Please comment for improvements:
static const char LARGEST_NEGATIVE[] = "-2147483648";
static char* GetDigits(int32_t x) {
char* buffer = (char*) calloc(sizeof(LARGEST_NEGATIVE), 1);
int negative = x < 0;
if (negative) {
if (x + (1 << 31) == 0) { // if x is the largest negative number
memcpy(buffer, LARGEST_NEGATIVE, sizeof(LARGEST_NEGATIVE));
return buffer;
}
x *= -1;
}
// storing digits in reversed order
int length = 0;
do {
buffer[length++] = x % 10 + '0';
x /= 10;
} while (x > 0);
if (negative) {
buffer[length++] = '-'; // appending minus
}
// reversing digits
for (int i = 0; i < length / 2; i++) {
char temp = buffer[i];
buffer[i] = buffer[length-1 - i];
buffer[length-1 - i] = temp;
}
return buffer;
}
//Fixed the answer from [10]
#include <iostream>
void CovertIntToString(unsigned int n1)
{
unsigned int n = INT_MIN;
char buffer[50];
int i = 0;
n = n1;
bool isNeg = n<0;
n1 = isNeg ? -n1 : n1;
while(n1!=0)
{
buffer[i++] = n1%10+'0';
n1=n1/10;
}
if(isNeg)
buffer[i++] = '-';
buffer[i] = '\0';
// Now we must reverse the string
for(int t = 0; t < i/2; t++)
{
buffer[t] ^= buffer[i-t-1];
buffer[i-t-1] ^= buffer[t];
buffer[t] ^= buffer[i-t-1];
}
if(n == 0)
{
buffer[0] = '0';
buffer[1] = '\0';
}
printf("%s", buffer);
}
int main() {
unsigned int x = 4156;
CovertIntToString(x);
return 0;
}
This function converts each digits of number into a char and chars add together
in one stack forming a string. Finally, string is formed from integer.
string convertToString(int num){
string str="";
for(; num>0;){
str+=(num%10+'0');
num/=10;
}
return str;
}

Tiny snippet for converting 4 hex characters to an integer in C

I need to parse strings of four hex characters to an integer. The characters appear inside a longer string, and there are no separators - I just know the offset they can be found in. The hex characters are case insensitive. Example with offset 3:
"foo10a4bar" -> 4260
I'm looking for a snippet that is
Short (too much code always creates complexity)
Simple (simple to understand and verify that it is correct)
Safe (invalid input is detected and signalled, no potential memory problems)
I'm a bit leery of using the 'sscanf' family of functions for this, but if there's a safe ANSI C solution using them, they can be used.
strtol is simple with good error handling:
const int OFFSET = 3, LEN = 4;
char hex[LEN + 1];
int i;
for(i = 0; i < LEN && str[OFFSET + i]; i++)
{
hex[i] = str[OFFSET + i];
if(!isxdigit((unsigned char) hex[i]))
{
// signal error, return
}
}
if(i != LEN)
{
// signal error, return
}
hex[LEN] = '\0';
char *end;
int result = (int) strtol(hex, &end, 16);
if(end != hex + LEN)
{
// signal error, return
}
It's usually best to use standard functions where you can, to get concise and simple code:
#define HEXLEN 4
long extract_hex(const char *src, size_t offset)
{
char hex[HEXLEN + 1] = { 0 };
long val;
if (strlen(src) < offset + HEXLEN)
return -1;
memcpy(hex, src + offset, HEXLEN);
if (strspn(hex, "0123456789AaBbCcDdEeFf") < HEXLEN)
return -1;
errno = 0;
val = strtol(hex, NULL, 16);
/* Out of range - can't occur unless HEXLEN > 7 */
if (errno)
return -1;
return val;
}
Here's my attempt
#include <assert.h>
static int h2d(char c) {
int x;
switch (c) {
default: x = -1; break; /* invalid hex digit */
case '0': x = 0; break;
case '1': x = 1; break;
case '2': x = 2; break;
/* ... */
case 'E': case 'e': x = 14; break;
case 'F': case 'f': x = 15; break;
}
return x;
}
int hex4(const char *src, int offset) {
int tmp, val = 0;
tmp = h2d(*(src+offset+0)); assert(tmp >= 0); val += tmp << 12;
tmp = h2d(*(src+offset+1)); assert(tmp >= 0); val += tmp << 8;
tmp = h2d(*(src+offset+2)); assert(tmp >= 0); val += tmp << 4;
tmp = h2d(*(src+offset+3)); assert(tmp >= 0); val += tmp;
return val;
}
Of course, instead of assert use your preferred method of validation!
And you can use it like this
int val = hex4("foo10a4bar", 3);
Here's an alternative based on character arithmetic:
int hexdigits(char *str, int ndigits)
{
int i;
int n = 0;
for (i=0; i<ndigits; ++i) {
int d = *str++ - '0';
if (d > 9 || d < 0)
d += '0' - 'A' + 10;
if (d > 15 || d < 0)
d += 'A' - 'a';
if (d > 15 || d < 0)
return -1;
n <<= 4;
n |= d;
}
return n;
}
It should handle digits in both cases, and work for both ASCII and EBCDIC. Using it for more than 7 digits invites integer overflow, and may make the use of -1 as an error value indistinguishable from a valid conversion.
Just call it with the offset added to the base string: e.g. w = hexdigits(buf+3, 4); for the suggested offset of 3 chars into a string stored in buf.
Edit: Here's a version with fewer conditions that is guaranteed to work for ASCII. I'm reasonably certain it will work for EBCDIC as well, but don't have any text of that flavor laying around to prove it.
Also, I fixed a stupid oversight and made the accumulator an int instead of unsigned short. It wouldn't affect the 4-digit case, but it made it overflow at only 16-bit numbers instead of the full capacity of an int.
int hexdigits2(char *str, int ndigits)
{
int i;
int n = 0;
for (i=0; i<ndigits; ++i) {
unsigned char d = *str++ - '0';
if (d > 9)
d += '0' - 'A' + 10;
if (d > 15)
d += 'A' - 'a';
if (d > 15)
return -1;
n <<= 4;
n |= d;
}
return n;
}
Usage is the same as the earlier version, but the generated code could be a bit smaller.
Here's my own try at it now that I thought about it for a moment - I'm not at all sure this is the best, so I will wait a while and then accept the answer that seems best to me.
val = 0;
for (i = 0; i < 4; i++) {
val <<= 4;
if (ptr[offset+i] >= '0' && ptr[offset+i] <= '9')
val += ptr[offset+i] - '0';
else if (ptr[offset+i] >= 'a' && ptr[offset+i] <= 'f')
val += (ptr[offset+i] - 'a') + 10;
else if (ptr[offset+i] >= 'A' && ptr[offset+i] <= 'F')
val += (ptr[offset+i] - 'A') + 10;
else {
/* signal error */
}
}
/* evaluates the first containing hexval in s */
int evalonehexFromStr( const char *s, unsigned long *val )
{
while( *s )
if( 1==sscanf(s++, "%04lx", val ) )
return 1;
return 0;
}
It works for exactly 4 hex-digits, eg:
unsigned long result;
if( evalonehexFromStr("foo10a4bar", &result) )
printf("\nOK - %lu", result);
If you need other hex-digit sizes, replace "4" to your size or take "%lx" for any hexval for values up to MAX_ULONG.
Code
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int offset = atoi(argv[2]);
argv[1][offset + 4] = '\0';
printf("%lu\n", strtol(argv[1] + offset, NULL, 0x10));
}
Usage
matt#stanley:$ make small_hex_converter
cc small_hex_converter.c -o small_hex_converter
matt#stanley:$ ./small_hex_converter f0010a4bar 3
4260

Resources