Can memory be the issue? - c

I was solving a question this is my code its working perfect on my local Work space with the below input and gives the expected output but in hacker rank it is a time out for compilation .
The problem statement is
In the given string:
occurs two times.
and occur one time each.
The remaining digits and don't occur at all.
Sample Input 1: lw4n88j12n1
Sample Output 1 : 0 2 1 0 1 0 0 0 2 0
Facing Issue with below input
Input:
9139f793308o0lo66h6vc13lgc697h0f6c32lu84445972k0o0l033od17c083yn5051d6j319hyo8j939n28d913015ns6zx5653x01211x12ch2526o65sg7xw6302141q9203s22l336319ll9yx4b597mr318a7943906750j4u152067nq83ne9f24thu96yd05173l47c803roxci45615f0w53i1sz913jj6za733l73tw6r66mq6p44sfhjr26h8e801z8zlcx2l1e65r2879xj3w3acv216196uq158o663y7oz2i5378v0v5w17762451t424352m23026r9o202i9785382o159e4gu1c8561157z5f1vqs5755465b8u728u956434mv944885li456628a994u7j5278m269n1pk8e46940q834h06il6h447888tr7ig72z10fe09k5g98h9bgt6z40v42s16pt6k3l3v45i83i01b9448g554741w766f2q7v31i085488h060e710p53076c6nm98pi946g8j2n6j8x29qa1ad48172y0u4818121p686bud89741201p54087u56g8scerv9pvhuo09re477zfb224i2c1325bj58jx4bk7b009f6446j5i95474p266i503r670n631x6940gwl71ejbx47imx576129248901765rnpu6l80084t0j1839f5y3409w2n403fu6ogw1170jmb6o5l520vg0703e0
Expected Output: 53 54 47 48 54 52 63 46 49 46
Thanks for the help in advance.
int main() {
int i;
int value;
char x;
int arr[10] = {0};
char * ptr =(char *)malloc(sizeof(char *));
/* Enter your code here. Read input from STDIN. Print output to STDOUT */
scanf("%s",ptr);
while(*ptr != '\0')
{
if(*ptr >= 65 && *ptr <=90 || *ptr >=97 && *ptr <=122){
*ptr++;
}
else{
x = *ptr;
value = atoi(&x);
switch (value)
{
case 0:
arr[0]++;
*ptr++;
break;
case 1:
arr[1]++;
*ptr++;
break;
case 2:
arr[2]++;
*ptr++;
break;
case 3:
arr[3]++;
*ptr++;
break;
case 4:
arr[4]++;
*ptr++;
break;
case 5:
arr[5]++;
*ptr++;
break;
case 6:
arr[6]++;
*ptr++;
break;
case 7:
arr[7]++;
*ptr++;
break;
case 8:
arr[8]++;
*ptr++;
break;
case 9:
arr[9]++;
*ptr++;
break;
}
}
}
for(i=0;i<=9;i++)
{
printf("%d ",arr[i]);
}
return 0;
}

Here is error: char * ptr =(char *)malloc(sizeof(char *));
sizeof(char *) Equal 4 or 8. You allocated memory for example 8 chars. When you write entire STDIN to ptr then you will overwrite memory on heap.
When you read from stdin you MUST specify max length example:
char str[256] = {'\0'}
scanf ("%255s",str); //Read max 255 chars
About your body of loop, this is better :)
uint8_t digit;
if (*ptr >= '0' && *ptr <= '9')
{
digit = (uint8_t)(*ptr - '0');
if (digit < 10)
{
arr[digit]++;
}
}
ptr++; //THERE IS NO STAR *
But you should use simple for with know length and
if (ptr[i] == '\0') break;
Hope it's helps.
Edit:
sizeof(char*) is 4 or 8 as suggested #dbush.

Related

C isnt running my switch statement

I am having issues in C with a switch case
#include <stdio.h>
#include <strings.h>
void runprogram(void); // parses the input file
void display(int, int); // prints out the requested lines
void clearmemory(void); // clears the choice for input file
int traceflag = 0; //determines if the output should say everything
char memory[128]; //the file name for the input
void runprogram (void)
{
FILE *ifp; //file loaded
int total=0; //accumulator total
int temp3;
int a, b, c, d, numcnt, operand, opcode, CNNN;
char ch, ch2;
printf("Current File: %s\n",memory);
if(traceflag==0)
printf("Trace is off\n");
else
printf("Trace is on\n");
ifp = fopen(memory,"r"); //opens the file
char line [128];
char NNN[3];
char Cnnn[4];
char C[1];
a=0;
b=0;
c=0;
d=0;
numcnt=0;
while(fgets(line,sizeof line,ifp)!= NULL) // read a line from a file
{
while ((ch = line[b]) != '\0' )
{
if ( numcnt == 1 )
{
if (isdigit(ch))
{
ch2 = line [b + 1];
if ( isdigit( ch2 ))
{
for (a = 0; a < 4; a++ )
{
Cnnn [ a ] = ch;
b++;
ch = line [ b ];
}
for (a=0;a<4;a++)
{
if (a==0)
opcode=Cnnn[a];
else
NNN[a-1]=Cnnn[a];
}
numcnt++;
b=0;
a=0;
}
}
}
else if ( isdigit(ch))
{
ch2 = line [ b + 1 ];
if ( ch2 == ' ' )
numcnt++;
}
else
ch = line [ b ];
b++;
ch = line [ b ];
}
fgets( line, sizeof(line), ifp);
numcnt=0;
b=0;
d=0;
for (c = 0; c < 3; c++)
{
NNN [ c ] = Cnnn [ d + 1 ];
d++;
}
sscanf(NNN, "%d", &operand);
opcode=opcode/10;
b=0;
d=0;
/*if(traceflag==1)
{
printf("Full Line: %s\n",line); //print the file
printf("Opcode: %d\n", opcode);
}*/
printf("Opcode: %d Operand: %d\n", opcode, operand);
switch ( opcode )
{
case 0: //0 - Halt
printf("Run finished \n");
break;
case 1: //1 - Load: Copy memory nnn to accumulator
total=operand;
break;
case 2: //2 - Store: Copy accumulator to memory nnn
operand=total;
break;
case 3: //3 - Add memory nnn to accumulator
total=total+operand;
break;
case 4: //4 - Subtract memory nnn from accumulator
total=total-operand;
break;
case 5:
//int temp3; //temp to hold entered number
printf("Enter a number: ");
scanf(" %d",&temp3);
total=temp3;
break;
case 6:
printf("Accumulator: %d\n",total);
break;
case 7:
if(total==0)
opcode=operand/10;
break;
case 8: //8 ‐ Branch to nnn if the accumulator > 0
if(total>0)
opcode=operand/10;
break;
case 9: //9 – Branch to nnn
opcode=operand/10;
break;
default:
total=0;
break;
}
}
printf("Run finished \n");
fclose(ifp);
}`
The opcode is always an int, and it is the right int to run the switch.
yet for some reason it skips over the switch runs through all the lines and never outputs anything from within the switch case.
the file loaded is
0 Rd 5000
1 st n 2017
2 ld zero 1014
3 st sum 2016
4 L: ld n 1017
5 Add sum 3016
6 St sum 2016
7 Ld n 1017
8 Sub one 4015
9 St n 2017
10 Brgt L 8004
11 Ld sum 1016
12 Wr 6000
13 Stop 0000
14 Zero: 0 0000
15 One: 1 0001
16 Sum: 0 0000
17 N: 0 0000
Im not quite sure whats wrong with if and im not sure whats wrong i have tried many things and cant find anything online to help. Any input would be appreciated and if you cant compile and run the program i will give all the code. since the code is over 200 lines long i felt it would be inappropriate to post it all, yet if i must i will.
The problem is in the line opcode=Cnnn[a];
opcode is an int and Cnnn[a] is a char when you assign an int to a char it assigns the int value of the character according to the ASCII encoding. Example:
#include<stdio.h>
int main()
{
int a;
char b = '1';
a = b;
printf("%d", a);
return 0;
}
This prints out 49 because according to ascii the character '1' corresponds to 49
EDIT:
Im not sure if this is the "Correct" approach to solve your problem but since we notice that the int values we get are 48 more than the int value we want we can do opcode = (Cnnn[a] - 48);

What is this following C language code snippet doing?

The following code snippet is a part of the program to evaluate a postfix expression. I am not too much experienced with C programming, so forgive me for my lesser knowledge. I do not understand what the highlighted part of the code is doing.
char str[100];
int i, data = -1, operand1, operand2, result;
/* Get the postfix expression from the user */
printf("Enter ur postfix expression:");
fgets(str, 100, stdin);
for (i = 0; i < strlen(str); i++)
{
if (isdigit(str[i]))
{
/*
* if the i/p char is digit, parse
* character by character to get
* complete operand
*/
data = (data == -1) ? 0 : data;
data = (data * 10) + (str[i] - 48); //What is happening here
continue;
}
It is converting the number in the string str to an actual number, digit per digit (or character per character if you will).
The line
data = (data * 10) + (str[i] - 48);
takes the number "so far" and adds the new digit to it, by multiplying the number by 10 and then adding the value of str[i] to it. The character str[i] is in the range '0' .. '9' and by subtracting 48 of it -- the ASCII value of '0' -- you get the value of the digit.
So if data is 95; for instance, and str[i] is '3', then data becomes 950 + ASCII code of '3' - ASCII code of '0', so data becomes 950 + 3 = 953.
data = (data * 10) + (str[i] - 48);
That line converts the value of str[i] to integer value and converts whole number (hence the multiplication by 10).
E.g.
'0' -> 0
'1' -> 1
E.g."100" -> 100
It assumes ASCII representation and hence uses 48. A more portable way would be to use '0' instead:
data = (data * 10) + (str[i] - '0');
According to your code snippet
**data = (data * 10) + (str[i] - 48);
this line will change your string to integer format
for example like you are giving input 235
then ASCII code of 2 is 50 and when you subtract with 48 then it will come 2.
now multiply you previous no (which is 0) by 10 and add 2. then it will become 2
next 3 will come which is having 51 ASCII and after subtract 48 will become 3
now multiply you previous no (which is 2) by 10 and add 3. then it will become 23 and so on.
like this you are converting string to an integer no.
For the better understanding print your values that is been generated at the intermediate instance since your not too much experienced in C Program have a printf statement so that you can understand the logic.
#include <stdio.h>
#include <string.h>
#include<ctype.h>
#include<conio.h>
int top = -1;
int stack[100];
/* push the given data into the stack */
void push (int data) {
stack[++top] = data;
}
/* Pop the top element from the stack */
int pop () {
int data;
if (top == -1)
return -1;
data = stack[top];
stack[top] = 0;
top--;
return (data);
}
int main() {
char str[100];
int i, data = -1, operand1, operand2, result;
/* Get the postfix expression from the user */
printf("Enter ur postfix expression:");
fgets(str, 100, stdin);
for (i = 0; i < strlen(str); i++) {
if (isdigit(str[i])) {
/*
* if the i/p char is digit, parse
* character by character to get
* complete operand
*/
data = (data == -1) ? 0 : data;
printf("%d value of str[i] ",str[i]); // returns the ascii value
data = (data * 10) + (str[i] - 48); //multiplies with ten and substracts with 48 so thst u get ur input number
printf("%d\n",data);
continue;
}
if (data != -1) {
/* if the i/p is operand, push it into the stack */
push(data);
}
if (str[i] == '+' || str[i] == '-'
|| str[i] == '*' || str[i] == '/') {
/*
* if the i/p is an operator, pop 2 elements
* from the stack and apply the operator
*/
operand2 = pop();
operand1 = pop();
if (operand1 == -1 || operand2 == -1)
break;
switch (str[i]) {
case '+':
result = operand1 + operand2;
/* push the result into the stack */
push(result);
break;
case '-':
result = operand1 - operand2;
push(result);
break;
case '*':
result = operand1 * operand2;
push(result);
break;
case '/':
result = operand1 / operand2;
push(result);
break;
}
}
data = -1;
}
if (top == 0)
printf("Output:%d\n", stack[top]);
else
printf("u have given wrong postfix expression\n");
getch();
return 0;
}
output:

convert particular array elements as integer value

I have declared an array char Buffer[100]="01 05 01 4A 63 41"; now the array looks like this
Buffer[0]='0'
Buffer[1]='1'
Buffer[2]=' '
Buffer[3]='0'
Buffer[4]='5'
i just want to convert these value to int `eg.:
Buffer[0]='0', Buffer[1]='1' to 0x01 (1)
Buffer[0]='0', Buffer[1]='5' to 0x05 (5)
... etc.
atoi()cannot be used since it converts all the Buffer value as integer.
How to convert a particular space delimited value sub-string to an integer?
My first solution works only for integers, and the following one works also for hexadecimal numbers. I wrote down the function which converts string representation of a hexadec. number into a decimal number. Then, as suggested by Jochim Pileborg, I used strtok to parse the given Buffer array.
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int hexToInt(char *tok)
{
int i,out=0, tens=1, digit;
for(i=strlen(tok)-1; i>=0; i--)
{
switch(tok[i])
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': digit=tok[i]-'0';
break;
case 'A': digit=10; break;
case 'B': digit=11; break;
case 'C': digit=12; break;
case 'D': digit=13; break;
case 'E': digit=14; break;
case 'F': digit=15; break;
}
out+=digit*tens;
tens*=16;
}
// printf("hex:%s int:%d ", tok, out);
return out;
}
int main()
{
char Buffer[100]="01 2A 10 15 20 25";
int intarr[100],current=0;
char *tok=malloc(20*sizeof(char));
tok=strtok(Buffer," ");
while(tok!=NULL)
{
intarr[current]=hexToInt(tok);
current++;
tok=strtok(NULL," ");
}
printf("\n");
}
You can treat Buffer as a string (which it is), and use e.g. strtok to "tokenize" the numbers on space boundary. Then use strtol to convert each "token" to a number.
But do note that strtok modifies the string, so if you don't want that you have to make a copy of the original Buffer and work on that copy.
Also note that as the numbers seems to be hexadecimal you can't use atoi because that function only parses decimal numbers. You have to use strtol which can handle any base from 2 to 36.
Consider this:
#include <stdio.h>
int main()
{
char Buffer[100] = "01 05 01 4A 63 41" ;
const char* h = &Buffer[0] ;
int i ;
while( *h != 0 )
{
if( sscanf( h, "%2x", &i ) == 1 )
{
printf( "0x%02X (%d)\n", i, i ) ;
}
h += 3 ;
}
return 0;
}
The output from which is:
0x01 (1)
0x05 (5)
0x01 (1)
0x4A (74)
0x63 (99)
0x41 (65)
I have assumed that all the values are hexadecimal, all two digits, and all separated by a single space (or rather a single non-hex-difgit character), and that the array is nul terminated. If either of these conditions are not true, the code will need modification. For example if the values may be variable length, then the format specifiers need changing, and, you should increment h until a space or nul is found, and if a space is found, increment once more.
You could write similar code using strtol() instead of sscanf() for conversion, but atoi() is specific to decimal strings, so could not be used.
If you are uncomfortable with the pointer arithmetic, then by array indexing the equivalent is:
#include <stdio.h>
int main()
{
char Buffer[100] = "01 05 01 4A 63 41" ;
int c = 0 ;
int i ;
while( *h != 0 )
{
if( sscanf( &Buffer[c], "%2x", &i ) == 1 )
{
printf( "0x%02X (%d)\n", i, i ) ;
}
c += 3 ;
}
return 0;
}
and the strtol() version if you prefer:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char Buffer[100] = "01 05 01 4A 63 41" ;
const char* h = &Buffer[0] ;
while( *h != 0 )
{
int i = strtol( h, 0, 16 ) ;
printf( "0x%02X (%d)\n", i, i ) ;
h += 3 ;
}
return 0;
}
You can use sscanf for this, e.g.:
int intarr[6];
sscanf(Buffer,"%d %d %d %d %d %d",&intarr[0],&intarr[1],&intarr[2],&intarr[3],&intarr[4],&intarr[5]);
You can cast Buffer[i] to int.
Then check its value, which will be in ASCII.
48->0
.
.
57->9
You can even compare the char to its ASCII value without casting
int CharToDigit(char c)
{
if(c>=48 && c<=57) // or as suggested if(c>='0' && c <='9')
return (int)c - 48; // subtract the ascii of 0
return -1; // not digit
}
For digits from A to F you'll have to subtract 55 from uppercase letters (65-10, 65 is ascii of A)
Then loop through the chars in Buffer sending them to the function: CharToDigit(Buffer[i]) and check the returned int.

Convert hex char[] to int[] in C 2 chars in 1 byte

I am trying to convert a char[] in hexadecimal format to int[] in hexadecimal.
Something like this:
hello --> 68656C6C6F --> [68, 65, 6C, 6C, 6F]
This is my code:
#include <stdio.h>
#include <string.h>
uint8_t* hex_decode(unsigned char *in, size_t len, uint8_t *out);
int main(void){
unsigned char word_in[17], word_out[33];//17:16+1, 33:16*2+1
int i, len = 0;
uint8_t* out;
while(len != 16){
printf("Set new word:");
fgets( word_in, sizeof( word_in), stdin);
len = strlen( word_in);
if( word_in[len-1]=='\n')
word_in[--len] = '\0';
for(i = 0; i<len; i++){
sprintf(word_out+i*2, "%02X", word_in[i]);
}
if(len != 16){
printf("Please, use a word of 16 chars long\n\n");
}
}
printf("%s", word_in);
printf("\n");
hex_decode(word_out, sizeof(word_out), out);
return 0;
}
uint8_t* hex_decode(unsigned char *in, size_t len, uint8_t *out)
{
unsigned int i, t, hn, ln;
for (t = 0,i = 0; i < len; i+=2,++t) {
hn = in[i] > '9' ? (in[i]|32) - 'a' + 10 : in[i] - '0';
ln = in[i+1] > '9' ? (in[i+1]|32) - 'a' + 10 : in[i+1] - '0';
out[t] = (hn << 4 ) | ln;
printf("%s",out[t]);
}
return out;
}
But after printing the word, I got a segmentation fault.
That function works perfect in arduino so I think it should works fine at my computer... Where is the problem?
See #dasblinkenlight answer for seg fault. To decode 2 bytes:
My 50 Cent... (a short version)
char hex[3];
char * phex;
int result;
for(int i = 0; i < 256; i++)
{
sprintf(hex, "%02X", i);
phex = hex;
result = ((*phex > 64 ? (*phex & 0x7) + 9 : *phex - 48) << 4) | (*(phex+1) > 64 ? (*(phex+1) & 0x7) + 9 : *(phex+1) - 48);
if(result != i)
{
printf("err %s %02X\n", hex, result);
}
}
Code above does no validation. This procedure returns -1 when input was invalid.
int h2i(char * ph)
{
int result;
if(*ph > 96 && *ph < 103) result = *ph - 87;
else if(*ph > 64 && *ph < 71) result = *ph - 55;
else if(*ph > 47 && *ph < 59) result = *ph - 48;
else return -1;
result <<= 4;
ph++;
if(*ph > 96 && *ph < 103) result |= *ph - 87;
else if(*ph > 64 && *ph < 71) result |= *ph - 55;
else if(*ph > 47 && *ph < 59) result |= *ph - 48;
else return -1;
return result;
}
But wait? A char can also be -1. Yes, after casting.
char * x = "FF";
char y;
int result;
result = h2i(x);
// if (result == -1) ...error...
y = (char)result;
You get a segmentation fault because you are passing the pointer out before making any assignments to it. Either the hex_decode need to take uint8_t **out_ptr and assign it a dynamically allocated array, or the caller needs to provide an array sufficient to hold the output of the conversion.
The reason why it "works" on another platform is that it exhibits undefined behavior: in arduino, the arbitrary value placed in the uninitialized pointer out happens to point to an unused location in memory. Writing to that location does not trigger segmentation fault, creating an illusion of working code.
i will just share my own code for this:
it converts any 8 hexadecimal char string into a integer number from [-2147483648. 2147483647]
input(argument) is 1 string(8+'\0'), output(returns) is a long int, MODIFY AS NECESSARY
#define N 8
long int hex2dec(char* hex){ /*conversor HEX 2 DEC*/
int i,j,n[N],l,neg;
long int dec=0;
for(i=0;i<N;i++){
n[i]=0;
}
l=strlen(hex);
neg=0;
if(hex[0]>='8'){
neg=1;
for(i=0;i<N;i++){
if(hex[i]=='0'){
hex[i]='F';
continue;
}
if(hex[i]=='1'){
hex[i]='E';
continue;
}
if(hex[i]=='2'){
hex[i]='D';
continue;
}
if(hex[i]=='3'){
hex[i]='C';
continue;
}
if(hex[i]=='4'){
hex[i]='B';
continue;
}
if(hex[i]=='5'){
hex[i]='A';
continue;
}
if(hex[i]=='6'){
hex[i]='9';
continue;
}
if(hex[i]=='7'){
hex[i]='8';
continue;
}
if(hex[i]=='8'){
hex[i]='7';
continue;
}
if(hex[i]=='9'){
hex[i]='6';
continue;
}
if(hex[i]=='A'){
hex[i]='5';
continue;
}
if(hex[i]=='B'){
hex[i]='4';
continue;
}
if(hex[i]=='C'){
hex[i]='3';
continue;
}
if(hex[i]=='D'){
hex[i]='2';
continue;
}
if(hex[i]=='E'){
hex[i]='1';
continue;
}
if(hex[i]=='F'){
hex[i]='0';
continue;
}
}
}
for(i=0;i<N;i++){
switch(hex[i]){
case '0':
n[i]=hex[i]-48; /* Ascii '0'=48 48-48=0*/
break;
case '1':
n[i]=hex[i]-48; /* Ascii '1'=49 49-48=1*/
break;
case '2':
n[i]=hex[i]-48;
break;
case '3':
n[i]=hex[i]-48;
break;
case '4':
n[i]=hex[i]-48;
break;
case '5':
n[i]=hex[i]-48;
break;
case '6':
n[i]=hex[i]-48;
break;
case '7':
n[i]=hex[i]-48;
break;
case '8':
n[i]=hex[i]-48;
break;
case '9':
n[i]=hex[i]-48;
break;
case 'A':
n[i]=hex[i]-55; /* Ascii 'A'=65 65-55=10*/
break;
case 'B':
n[i]=hex[i]-55; /* Ascii 'B'=66 66-55=11*/
break;
case 'C':
n[i]=hex[i]-55;
break;
case 'D':
n[i]=hex[i]-55;
break;
case 'E':
n[i]=hex[i]-55;
break;
case 'F':
n[i]=hex[i]-55;
break;
}
}
for(i=0,j=l;i<l;i++,j--){
dec=dec+(n[j-1]*pow(16,i));
}
if(neg==1){
dec=0-dec;
dec=dec-1;
}
return dec;
}
change
uint8_t *out;//region is not ensured
to
uint8_t out[sizeof(word_out)/2];
change
hex_decode(word_out, sizeof(word_out), out);//sizeof(word_out) is 33, must to 32
to
hex_decode(word_out, strlen(word_out), out);//strlen(word_out) or len * 2 or sizeof(word_out) -1
change
printf("%s",out[t]);//out is not string
to
printf("%02X ",out[t]);
The program looks complicated comparing what you want to do.
if you want to print the hexadecimal ascii code of a charachter, you can simply use
printf("%02X",'K'); // this will display the code ascii of 'K' in hexadecimal
If you want to print your word in code ascii in another char array. you can use sprintf():
int main() {
char word_in[17]="hello", word_out[33];
char *pi = word_in, *po = word_out;
word_out[0]=0;
for (;*pi;po+=2,pi++)
sprintf(po,"%02X",*pi);
printf("%s\n", word_out);
}
A charachetr is saved in binary format in the memory. this binary format represent the code ascii of the charachter. And when you want to print its content:
when using "%d": this will print the code ascii as integer
when using "%x": this will print the code ascii as hexadecimal
when using "%c": this will print the charachter

How to turn a hex string into an unsigned char array?

For example, I have a cstring "E8 48 D8 FF FF 8B 0D" (including spaces) which needs to be converted into the equivalent unsigned char array {0xE8,0x48,0xD8,0xFF,0xFF,0x8B,0x0D}. What's an efficient way to do this? Thanks!
EDIT: I can't use the std library... so consider this a C question. I'm sorry!
This answers the original question, which asked for a C++ solution.
You can use an istringstream with the hex manipulator:
std::string hex_chars("E8 48 D8 FF FF 8B 0D");
std::istringstream hex_chars_stream(hex_chars);
std::vector<unsigned char> bytes;
unsigned int c;
while (hex_chars_stream >> std::hex >> c)
{
bytes.push_back(c);
}
Note that c must be an int (or long, or some other integer type), not a char; if it is a char (or unsigned char), the wrong >> overload will be called and individual characters will be extracted from the string, not hexadecimal integer strings.
Additional error checking to ensure that the extracted value fits within a char would be a good idea.
You'll never convince me that this operation is a performance bottleneck.
The efficient way is to make good use of your time by using the standard C library:
static unsigned char gethex(const char *s, char **endptr) {
assert(s);
while (isspace(*s)) s++;
assert(*s);
return strtoul(s, endptr, 16);
}
unsigned char *convert(const char *s, int *length) {
unsigned char *answer = malloc((strlen(s) + 1) / 3);
unsigned char *p;
for (p = answer; *s; p++)
*p = gethex(s, (char **)&s);
*length = p - answer;
return answer;
}
Compiled and tested. Works on your example.
Iterate through all the characters.
If you have a hex digit, the number is (ch >= 'A')? (ch - 'A' + 10): (ch - '0').
Left shift your accumulator by four bits and add (or OR) in the new digit.
If you have a space, and the previous character was not a space, then append your current accumulator value to the array and reset the accumulator back to zero.
If you know the length of the string to be parsed beforehand (e.g. you are reading something from /proc) you can use sscanf with the 'hh' type modifier, which specifies that the next conversion is one of diouxX and the pointer to store it will be either signed char or unsigned char.
// example: ipv6 address as seen in /proc/net/if_inet6:
char myString[] = "fe80000000000000020c29fffe01bafb";
unsigned char addressBytes[16];
sscanf(myString, "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx
%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", &addressBytes[0],
&addressBytes[1], &addressBytes[2], &addressBytes[3], &addressBytes[4],
&addressBytes[5], &addressBytes[6], &addressBytes[7], &addressBytes[8],
&addressBytes[9], &addressBytes[10], addressBytes[11],&addressBytes[12],
&addressBytes[13], &addressBytes[14], &addressBytes[15]);
int i;
for (i = 0; i < 16; i++){
printf("addressBytes[%d] = %02x\n", i, addressBytes[i]);
}
Output:
addressBytes[0] = fe
addressBytes[1] = 80
addressBytes[2] = 00
addressBytes[3] = 00
addressBytes[4] = 00
addressBytes[5] = 00
addressBytes[6] = 00
addressBytes[7] = 00
addressBytes[8] = 02
addressBytes[9] = 0c
addressBytes[10] = 29
addressBytes[11] = ff
addressBytes[12] = fe
addressBytes[13] = 01
addressBytes[14] = ba
addressBytes[15] = fb
use the "old" sscanf() function:
string s_hex = "E8 48 D8 FF FF 8B 0D"; // source string
char *a_Char = new char( s_hex.length()/3 +1 ); // output char array
for( unsigned i = 0, uchr ; i < s_hex.length() ; i += 3 ) {
sscanf( s_hex.c_str()+ i, "%2x", &uchr ); // conversion
a_Char[i/3] = uchr; // save as char
}
delete a_Char;
For a pure C implementation I think you can persuade sscanf(3) to do what you what. I believe this should be portable (including the slightly dodgy type coercion to appease the compiler) so long as your input string is only ever going to contain two-character hex values.
#include <stdio.h>
#include <stdlib.h>
char hex[] = "E8 48 D8 FF FF 8B 0D";
char *p;
int cnt = (strlen(hex) + 1) / 3; // Whether or not there's a trailing space
unsigned char *result = (unsigned char *)malloc(cnt), *r;
unsigned char c;
for (p = hex, r = result; *p; p += 3) {
if (sscanf(p, "%02X", (unsigned int *)&c) != 1) {
break; // Didn't parse as expected
}
*r++ = c;
}
The old C way, do it by hand ;-) (there is many shorter ways, but I'm not golfing, I'm going for run-time).
enum { NBBYTES = 7 };
char res[NBBYTES+1];
const char * c = "E8 48 D8 FF FF 8B 0D";
const char * p = c;
int i = 0;
for (i = 0; i < NBBYTES; i++){
switch (*p){
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
res[i] = *p - '0';
break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
res[i] = *p - 'A' + 10;
break;
default:
// parse error, throw exception
;
}
p++;
switch (*p){
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
res[i] = res[i]*16 + *p - '0';
break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
res[i] = res[i]*16 + *p - 'A' + 10;
break;
default:
// parse error, throw exception
;
}
p++;
if (*p == 0) { continue; }
if (*p == ' ') { p++; continue; }
// parse error, throw exception
}
// let's show the result, C style IO, just cout if you want C++
for (i = 0 ; i < 7; i++){
printf("%2.2x ", 0xFF & res[i]);
}
printf("\n");
Now another one that allow for any number of digit between numbers, any number of spaces to separate them, including leading or trailing spaces (Ben's specs):
#include <stdio.h>
#include <stdlib.h>
int main(){
enum { NBBYTES = 7 };
char res[NBBYTES];
const char * c = "E8 48 D8 FF FF 8B 0D";
const char * p = c;
int i = -1;
res[i] = 0;
char ch = ' ';
while (ch && i < NBBYTES){
switch (ch){
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
ch -= '0' + 10 - 'A';
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
ch -= 'A' - 10;
res[i] = res[i]*16 + ch;
break;
case ' ':
if (*p != ' ') {
if (i == NBBYTES-1){
printf("parse error, throw exception\n");
exit(-1);
}
res[++i] = 0;
}
break;
case 0:
break;
default:
printf("parse error, throw exception\n");
exit(-1);
}
ch = *(p++);
}
if (i != NBBYTES-1){
printf("parse error, throw exception\n");
exit(-1);
}
for (i = 0 ; i < 7; i++){
printf("%2.2x ", 0xFF & res[i]);
}
printf("\n");
}
No, it's not really obfuscated... but well, it looks like it is.

Resources