1

I have a basic question regarding a c problem I'm having. My input char array would be something like:

'DABC95C1' 

and I want to make an uint8_t array out of it

0xDA 0xBC 0x95 0xC1

I have easily access to each char but I dont know how I can form 0xDA. Is there function in c or can i just cast it?

melpomene
  • 84,125
  • 8
  • 85
  • 148
vicR
  • 789
  • 4
  • 23
  • 52

3 Answers3

4

Use the strtoull function to convert a string to a number in a given base. Then just shift out the desired bytes. Such as:

#include <stdio.h>
#include <stdlib.h>

int main(void) {

    unsigned long long res = strtoull("DABC95C1", NULL, 16);

    printf("%hhx, %hhx, %hhx, %hhx",
        (unsigned char)res,
        (unsigned char)((res >> 8)   &  0xFF),
        (unsigned char)((res >> 16)  &  0xFF),
        (unsigned char)((res >> 24)  &  0xFF)
    );

    return 0;
}

result:

c1, 95, bc, da

Demo

Notes:

As your requirement is to get an array of bytes, you might be tempted to do something like

uint8_t *arr = (uint8_t*)&res;

But here are two caveats in this:

1) I is a strict aliasing rule violation (you can somehow to work around it by replacing uint8_t with char)
2) The order of the returned bytes will be implementation specific (endianness dependent) and thus not portable. Also note that the result is unsigned long long, so you might get extra padding zeros as either the beginning of the array or in the end of it.

Eugene Sh.
  • 17,802
  • 8
  • 40
  • 61
  • So sad. I was about to upvote... But, now, I'm in doubt... (It's not 100 % perfect - you know.) – Scheff's Cat May 28 '18 at 14:23
  • 1
    @Scheff The OP wanted an array as well, not just printing. The fishing pole, you know... – Eugene Sh. May 28 '18 at 14:23
  • 1
    It is fairly safe to assume that `uint8_t` is a character type, because if there exists no character type of 8 bits for the given system, then `uint8_t` won't exist either. Which is only applicable to exotic, obsolete DSPs and the like, for which nobody needs code compatibility. – Lundin May 28 '18 at 14:41
  • @Lundin Yes, and frankly speaking we are doing it at where I am working. But I guess it's more like to match the previously designed code, as I I was doing it from scratch I would try to do it as close to the standard as possible. – Eugene Sh. May 28 '18 at 14:44
  • Very limited number of bytes. Up to 8. What if the string is more than 16byte long? – 0___________ May 28 '18 at 16:53
  • @PeterJ_01 Then a different approach is required. – Eugene Sh. May 28 '18 at 16:54
  • Em, is it necessary to cast reference to "uint8_t*" when "arr" casts it anyway? (Don't Repeat Yourself etc.). Think, could be better to left it as "uint8_t *arr = &res;" – Gromov Anton May 29 '18 at 07:37
1

Any size string in the chosen order. Portable digit conversion and it optimizes very well on the ASCII systems. https://godbolt.org/g/Ycah1e

#include <stdio.h>
#include <stdint.h>
#include <string.h>

int CharToDigit(const char c);
void *StringToTable(const char *str, const void *buff, const int order)
{
    uint8_t *ptr = (uint8_t *)buff;
    size_t len;

    int incr = order ? 1 : -1;

    if(buff && str)
    {
        len = strlen(str);

        if(len &1) return NULL;

        ptr += order ? 0 : len / 2 - 1;

        while(*str)
        {
            int d1 = CharToDigit(*str++);
            int d2 = CharToDigit(*str++);

            if(d1 == -1 || d2 == -1) return NULL;           
            *ptr = d1 * 16 + d2;
            ptr += incr;
        }
    }
    return buff;
}

int main(void) {

    int index = 0;
    char *str = "78deAc8912fF0f3B";
    uint8_t buff[strlen(str) / 2];

    StringToTable(str, buff, 0);

    printf("String: %s\nResult: ", str);
    for(index = 0; index < strlen(str) / 2; index++ )
    {
        printf("[0x%02hhx]", buff[index] );
    }
    printf("\n");

    StringToTable(str, buff, 1);

    printf("String: %s\nResult: ", str);
    for(index = 0; index < strlen(str) / 2; index++ )
    {
        printf("[0x%02hhx]", buff[index] );
    }
    printf("\n");

    return 0;
}

int CharToDigit(const char c)
{
    switch(c)
    {
        case 'a':
        case 'A':
            return 10;
        case 'b':
        case 'B':
            return 11;
        case 'c':
        case 'C':
            return 12;
        case 'd':
        case 'D':
            return 13;
        case 'e':
        case 'E':
            return 14;
        case 'f':
        case 'F':
            return 15;
        case '0':
            return 0;
        case '1':
            return 1;
        case '2':
            return 2;
        case '3':
            return 3;
        case '4':
            return 4;
        case '5':
            return 5;
        case '6':
            return 6;
        case '7':
            return 7;
        case '8':
            return 8;
        case '9':
            return 9;
        default:
            return -1;
    }
}
0___________
  • 60,014
  • 4
  • 34
  • 74
0

You can conver a character to an int like so

static inline int char2int(char Ch)
{
    return(Ch>='0'&&Ch<='9')?(Ch-'0'):(Ch-'A'+10); 
    //assuming correct input with no lowercase letters
}

Two characters then with

static inline 
int chars2int(unsigned char const Chars[2])
{
    return (char2int(Chars[0])<<4)|(char2int(Chars[1]));
}

And several characters by converting each pair:

static inline int char2int(char Ch)
{
    return(Ch>='0'&&Ch<='9')?(Ch-'0'):(Ch-'A'+10);
}
static inline 
int chars2int(unsigned char const Chars[2])
{
    return (char2int(Chars[0])<<4)|(char2int(Chars[1]));
}
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
    char const inp[] = "DABC95C1";
    assert((sizeof(inp)-1)%2==0);
    unsigned i;
    unsigned char out[(sizeof(inp)-1)/2];
    for(i=0;i<sizeof(inp);i+=2){
        out[i/2]=chars2int((unsigned char*)inp+i);
    }
    for(i=0;i<sizeof(out);i++)
        printf("%2x\n", out[i]);
}
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142