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?
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.
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;
}
}
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]);
}
Related
i have an uint8_t Variable which contains a substring of 4 hexadecimal variables. Example:
uint8_t String[10] = "00AABBCC";
I would like to take these 4 hex Variables into different hex values:
uint8_t Data_Byte[4];
Data_Byte[0]=0x00;
Data_Byte[1]=0xAA;
Data_Byte[2]=0xBB;
Data_Byte[3]=0xCC;
How can I take these 4 substrings into 4 different uint8_t Variables?
You can use sscanf to parse each two-character pair in the string into a number:
uint8_t arr[strlen(String) / 2];
for (int i = 0; i < strlen(String); i += 2) {
sscanf(String + i, "%2hhx", &arr[i / 2]);
}
If you're developing on a system with limited sscanf support, you can use something like this:
for (int i = 0; i < strlen(String); i += 2) {
uint8_t val1 = isdigit(String[i]) ? (String[i] - '0') : (String[i] - 'A' + 10);
uint8_t val2 = isdigit(String[i + 1]) ? (String[i + 1] - '0') : (String[i + 1] - 'A' + 10);
arr[i / 2] = val1 << 4 | val2;
}
With your stipulation the strings will represent 4 bytes, this a far-easier-to-read-and-understand solution IMO. I have no comment on efficiency.
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <arpa/inet.h>
bool convert(const uint8_t* strValue, uint8_t* cvrtArray)
{
// make 2nd parameter non-NULL for better error checking
errno = 0;
char* endptr = NULL;
// convert to unsigned long
unsigned long val = strtoul((const char*)strValue, &endptr, 16);
// do some error checking, this probably needs some improvements
if (errno == ERANGE && val == ULONG_MAX)
{
fprintf(stderr, "Overflow\n");
return false;
}
else if ((strValue != NULL) && (*endptr != '\0'))
{
fprintf(stderr, "Cannot convert\n");
return false;
}
// potential need to flip the bytes (your string is big endian, and the
// test machine on godbolt is little endian)
val = htonl(val);
// copy to our array
memcpy(cvrtArray, &val, 4);
return true;
}
int main(void)
{
uint8_t Data_Byte[4] = { 0 };
uint8_t String[10] = "00AABBCC";
if (convert(String, Data_Byte) == true)
{
for(size_t i=0; i<sizeof Data_Byte; i++)
{
printf("Data_Byte[%zu] = 0x%02" PRIX8 "\n", i, Data_Byte[i]);
}
}
else
{
fprintf(stderr, "There was a problem converting %s to byte array\n", String);
}
return 0;
}
code in action
I took some inspiration from 0___________ and made my own:
static char digits[] = "0123456789ABCDEF";
void convert(uint8_t *chrs, uint8_t *buff)
{
size_t len = strlen((char *)chrs);
size_t i;
for(i = 0; i < len; i+=2) {
buff[i / 2] = (strchr(digits, chrs[i]) - digits);
buff[i / 2] += (strchr(digits, chrs[i+1]) - digits) << 4;
}
if(i<len)
buff[i / 2] = (strchr(digits, chrs[i]) - digits);
}
The changes are that I find it much more natural to do a complete element in every iteration. To account for odd length input strings, I just added an if statement in the end that takes care of it. This can be removed if input strings always have even length. And I skipped returning the buffer for simplicity. However, as 0___________ pointed out in comments, there are good reasons to return a pointer to the output buffer. Read about those reasons here: c++ memcpy return value
static char digits[] = "0123456789ABCDEF";
uint8_t *convert(uint8_t *chrs, uint8_t *buff)
{
size_t len = strlen((char *)chrs);
for(size_t i = 0; i < len; i++)
{
int is_first_digit = !(i & 1);
int shift = is_first_digit << 2;
buff[i / 2] += (strchr(digits, chrs[i]) - digits) << shift;
}
return buff;
}
int main(void)
{
uint8_t String[] = "00AABBCC";
uint8_t buff[4];
convert(String, buff);
for(size_t i = 0; i < sizeof(buff); i++)
{
printf("%hhx", buff[i]); // I know it is wrong format
}
}
https://godbolt.org/z/9c8aexTvq
Or even faster solution:
int getDigit(uint8_t ch)
{
switch(ch)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return ch - '0';
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
return ch - 'A' + 10;
}
return 0;
}
uint8_t *convert(uint8_t *chrs, uint8_t *buff)
{
size_t len = strlen((char *)chrs);
for(size_t i = 0; i < len; i++)
{
int is_first_digit = !(i & 1);
int shift = is_first_digit << 2;
buff[i / 2] += (getDigit(chrs[i])) << shift;
}
return buff;
}
Remember: use functions for this kind of tasks. Do not program in main.
I've encountered an issue with heap deallocation using free() in my tokenizer. The tokenizer is part of a recursive descent parsing calculator, which works flawlessly otherwise. But upon incorporation of a call to the deallocation function, it behaves erratically. While realistically, the calculator will likely never come close to exhausting its heap, writing a program with a memory leak is just poor practice.
tokenize.h
#define OPERAND 0
#define OPERATOR 1
#define PARENTHESIS 2
#define TERMINAL 3
#define ADD '+'
#define SUBTRACT '-'
#define MULTIPLY '*'
#define DIVIDE '/'
#define EXPONENT '^'
#define L_PARENTHESIS '('
#define R_PARENTHESIS ')'
typedef struct {
int id;
char *value;
} token;
int token_count();
token *tokenize();
void deallocate();
tokenize.c
#include <stdio.h>
#include <stdlib.h>
#include "tokenize.h"
int token_count(char string[]) {
int i = 0;
int count = 0;
while (string[i] != '\0') {
if (string[i] >= '0' && string[i] <= '9') {
while (1) {
i++;
if (string[i] >= '0' && string[i] <= '9') {
continue;
} else {
break;
}
}
count++;
continue;
}
switch (string[i]) {
case ADD:
case SUBTRACT:
case MULTIPLY:
case DIVIDE:
case EXPONENT:
case L_PARENTHESIS:
case R_PARENTHESIS:
count++;
i++;
continue;
default:
return 0;
break;
}
}
return count;
}
token *tokenize(char string[]) {
int i = 0;
token *ret;
int count = token_count(string);
if (!count) {
return ret;
}
ret = malloc((count + 1) * sizeof(token));
ret[count].id = TERMINAL;
int ret_ind = 0;
while (string[i] != '\0') {
if (string[i] >= '0' && string[i] <= '9') {
ret[ret_ind].id = OPERAND;
int size = 0;
int j = i;
while (1) {
size++;
j++;
if (string[j] >= '0' && string[j] <= '9') {
continue;
} else {
break;
}
}
ret[ret_ind].value = malloc(size * sizeof(char) + 1);
ret[ret_ind].value[size + 1] = '\0';
for(int k = 0; k < size; k++) {
ret[ret_ind].value[k] = string[i + k];
}
i = j;
ret_ind++;
continue;
}
switch (string[i]) {
case ADD:
case SUBTRACT:
case MULTIPLY:
case DIVIDE:
case EXPONENT:
ret[ret_ind].id = OPERATOR;
ret[ret_ind].value = malloc(2 * sizeof(char));
ret[ret_ind].value[0] = string[i];
ret[ret_ind].value[1] = '\0';
ret_ind++;
i++;
continue;
case L_PARENTHESIS:
ret[ret_ind].id = PARENTHESIS;
ret[ret_ind].value = malloc(2 * sizeof(char));
ret[ret_ind].value[0] = L_PARENTHESIS;
ret[ret_ind].value[1] = '\0';
ret_ind++;
i++;
continue;
case R_PARENTHESIS:
ret[ret_ind].id = PARENTHESIS;
ret[ret_ind].value = malloc(2 * sizeof(char));
ret[ret_ind].value[0] = R_PARENTHESIS;
ret[ret_ind].value[1] = '\0';
ret_ind++;
i++;
continue;
default:
break;
}
break;
}
return ret;
}
void deallocate(token *in) {
int i = 0;
while (1) {
free(in[i].value);
i++;
if (in[i].id == TERMINAL) {
break;
}
}
free(in);
return;
}
There are multiple problems in your code:
in case the input line has no tokens or a syntax error, you return ret uninitialized from tokenize. You should return NULL instead.
ret[ret_ind].value[size + 1] = '\0'; stores the null terminator one step too far in the allocated array. It should be ret[ret_ind].value[size] = '\0';
malloc(size * sizeof(char) + 1) is inconsistent: if you insist on using sizeof(char), which is 1 by definition, you should write malloc((size + 1) * sizeof(char)), but it is idiomatic to use malloc(size + 1) in C and you could also replace multiple lines of code with a simple ret[ret_ind].value = strndup(string + i, k);
the cases for L_PARENTHESIS and R_PARENTHESIS could be merged into a single block.
the deallocation loop should stop when you reach the TERMINAL token. As currently coded, you cannot handle an empty list, which you should not produce, but it is better to make utility functions more resilient to later changes.
void deallocate(token *in) {
if (in) {
for (int i = 0; in[i] != TERMINAL; i++)
free(in[i].value);
free(in);
}
}
the prototypes in token.h should include the typed argument lists.
Here is a simplified version:
#include <stdio.h>
#include <stdlib.h>
#include "tokenize.h"
int token_count(const char *string) {
int count = 0;
int i = 0;
while (string[i] != '\0') {
switch (string[i++]) {
case ' ':
continue;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
i += strspn(string + i, "0123456789");
continue;
case ADD:
case SUBTRACT:
case MULTIPLY:
case DIVIDE:
case EXPONENT:
case L_PARENTHESIS:
case R_PARENTHESIS:
count++;
continue;
default:
return -1;
}
}
return count;
}
token *tokenize(const char *string) {
int count = token_count(string);
if (count <= 0)
return NULL;
token *ret = malloc((count + 1) * sizeof(token));
int i = 0;
int ret_ind = 0;
while (string[i] != '\0') {
if (string[i] >= '0' && string[i] <= '9') {
int size = strspn(string + i, "0123456789");
ret[ret_ind].id = OPERAND;
ret[ret_ind].value = strndup(string + i, size);
ret_ind++;
i += size;
continue;
}
switch (string[i]) {
case ' ':
i++;
continue;
case ADD:
case SUBTRACT:
case MULTIPLY:
case DIVIDE:
case EXPONENT:
ret[ret_ind].id = OPERATOR;
ret[ret_ind].value = malloc(2);
ret[ret_ind].value[0] = string[i];
ret[ret_ind].value[1] = '\0';
ret_ind++;
i++;
continue;
case L_PARENTHESIS:
case R_PARENTHESIS:
ret[ret_ind].id = PARENTHESIS;
ret[ret_ind].value = malloc(2);
ret[ret_ind].value[0] = string[i];
ret[ret_ind].value[1] = '\0';
ret_ind++;
i++;
continue;
default:
break;
}
break;
}
ret[ret_ind].id = TERMINAL;
return ret;
}
void deallocate(token *in) {
if (in) {
for (int i = 0; in[i] != TERMINAL; i++)
free(in[i].value);
free(in);
}
}
Here are additional remarks for the rest of the code:
why clear the screen on entry and exit?
you should test for end of file in the main loop:
if (!fgets(user_in, 1024, stdin))
break;
you should strip the newline efficiently:
#include <string.h>
user_in[strcspn(user_in, "\n")] = '\0';
then you can simplify the test for exit:
if (!strcmp(user_in, "exit"))
break;
no need to clear user_in after solve()
you could simplify testing by solving the command line arguments:
for (int i = 1; i < argc; i++)
solve(argv[i]);
you should ignore white space and accept empty lines
you should use "%.17g instead of %lf. Note that the l is mandatory
for scanf() for a double type, but ignored for printf, because
float arguments are converted to double when passed to vararg
functions like printf.
you should use a context structure and pass a pointer to it
to parse and its helper functions to avoid global variables
as you can see in try_add_sub and try_mul_div, it would simplify
the switch to unify token types and avoid the OPERATOR classification.
the parser is too complicated: you should use recursive descent more
directly: try_add_sub should first call try_mul_div and iterate on
additive operators, calling try_mul_div for each subsequent operand.
Similarly, try_mul_div should first call try_exp and try_exp would
call try_primitive which would handle parentheses and constants.
this approach consumes one token at a time, which can be read from
the expression source on the fly, bypassing the need for tokenizing the whole string.
you should accept the full number syntax for constants, which is easy with strtod().
Here is a simplified version along these directions:
//---- tokenize.h ----
#define TERMINAL 0
#define OPERAND 1
#define ERROR 2
#define ADD '+'
#define SUBTRACT '-'
#define MULTIPLY '*'
#define DIVIDE '/'
#define EXPONENT '^'
#define L_PARENTHESIS '('
#define R_PARENTHESIS ')'
#define SYNTAX_ERROR 1
#define PAREN_ERROR 2
typedef struct context {
char *p;
char *nextp;
int parenthesis_balance;
int error_code;
double value;
} context;
int this_token(context *cp);
void skip_token(context *cp);
//---- tokenize.c ----
#include <stdlib.h>
//#include "tokenize.h"
int this_token(context *cp) {
char *p = cp->p;
for (;;) {
switch (*p) {
case '\0':
cp->nextp = p;
return TERMINAL;
case ' ':
case '\t':
case '\n':
/* ignore white space */
p++;
continue;
case ADD:
case SUBTRACT:
case MULTIPLY:
case DIVIDE:
case EXPONENT:
case L_PARENTHESIS:
case R_PARENTHESIS:
/* single character operators */
cp->nextp = p + 1;
return *p;
default:
/* try and parse as a number constant */
cp->value = strtod(p, &cp->nextp);
if (cp->nextp > p)
return OPERAND;
return ERROR;
}
}
}
void skip_token(context *cp) {
cp->p = cp->nextp;
}
//---- parse.h ----
int parse(char expression[], double *result);
void solve(char expression[]);
//---- parse.c ----
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
//#include "tokenize.h"
//#include "parse.h"
/* expression parsers return non zero upon error */
int try_add_sub(context *cp, double *result);
int try_mul_div(context *cp, double *result);
int try_exp(context *cp, double *result);
int try_primary(context *cp, double *result);
int try_add_sub(context *cp, double *result) {
if (try_mul_div(cp, result))
return 1;
for (;;) {
double operand;
switch (this_token(cp)) {
case ADD:
skip_token(cp);
if (try_mul_div(cp, &operand))
return 1;
*result += operand;
continue;
case SUBTRACT:
skip_token(cp);
if (try_mul_div(cp, &operand))
return 1;
*result -= operand;
continue;
}
return 0;
}
}
int try_mul_div(context *cp, double *result) {
if (try_exp(cp, result))
return 1;
for (;;) {
double operand;
switch (this_token(cp)) {
case MULTIPLY:
skip_token(cp);
if (try_exp(cp, &operand))
return 1;
*result *= operand;
continue;
case DIVIDE:
skip_token(cp);
if (try_exp(cp, &operand))
return 1;
*result /= operand;
continue;
}
return 0;
}
}
int try_exp(context *cp, double *result) {
if (try_primary(cp, result))
return 1;
if (this_token(cp) == EXPONENT) {
double operand;
skip_token(cp);
if (try_exp(cp, &operand))
return 1;
*result = pow(*result, operand);
}
return 0;
}
int try_primary(context *cp, double *result) {
switch (this_token(cp)) {
case OPERAND:
skip_token(cp);
*result = cp->value;
return 0;
case L_PARENTHESIS:
skip_token(cp);
cp->parenthesis_balance++;
if (try_add_sub(cp, result))
return 1;
cp->parenthesis_balance--;
if (this_token(cp) != R_PARENTHESIS) {
cp->error_code = PAREN_ERROR;
return 1;
}
skip_token(cp);
return 0;
}
cp->error_code = SYNTAX_ERROR;
return 1;
}
/* parse and evaluate an expression, return error code, update result */
int parse(char expression[], double *result) {
context cc;
cc.nextp = cc.p = expression;
cc.parenthesis_balance = 0;
cc.error_code = 0;
cc.value = 0;
if (try_add_sub(&cc, result))
return cc.error_code;
if (this_token(&cc) != TERMINAL)
return SYNTAX_ERROR;
return 0;
}
void solve(char expression[]) {
double result = 0;
switch (parse(expression, &result)) {
case 0:
printf(" %.17g\n", result);
break;
case SYNTAX_ERROR:
printf("ERROR: Syntax\n");
break;
case PAREN_ERROR:
printf("ERROR: Unbalanced parenthesis\n");
break;
}
}
//---- calculator.c ----
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include "parse.h"
int main(int argc, char **argv) {
for (int i = 1; i < argc; i++)
solve(argv[i]);
if (argc == 1) {
char user_in[1024];
char *p;
printf("Terminal Calculator\n");
printf("Type 'exit' to terminate\n\n");
for (;;) {
printf("=> ");
if (!fgets(user_in, sizeof user_in, stdin)) {
printf("\n");
break;
}
/* strip trailing newline */
user_in[strcspn(user_in, "\n")] = '\0';
/* skip initial white space */
p = user_in + strspn(user_in, " \t");
/* ignore empty and comment lines */
if (*p == '\0' || *p == '#')
continue;
/* trap exit command */
if (!strcmp(p, "exit"))
break;
solve(p);
}
}
return 0;
}
Write a C function, that accepts a null-terminated string, containing a hexadecimal string, and returns the integer value. You cannot call any C library function, except strlen() to code the function. The decimal string will only contain 0-4 ASCII characters from ‘0’ through ‘9’ and ‘A’ through ‘F’. No error handling is required. If the string is empty, then return a value of 0.
I've constantly tried fixing my errors, but once I fix them, new errors pop up, resulting in me being confused.
#include <stdlib.h> /*used for EXIT_SUCCESS */
#include <stdio.h> /*used for printf */
#include <string.h> /* used for strlen */
#include <stdbool.h> /* used for bool */
#include <math.h>
unsigned int hexStringTouint(const char str[], int length, int n[])
{
int i, j;
int intvalue = 0;
int digit;
for(i = (length-1), j = 0; i --, j++)
{
if(n[i]>='0' && n[i] <='9')
{
digit = n[i] - 0x30;
}
else if(n[i]>= 'A' && n[i] <= 'F')
{
switch(n[i])
{
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;
}
}
intvalue += digit*pow(16,j);
}
printf("int value is %d\n", intvalue);
return 0;
}
int main(void)
{
int i, length, intvalue;
unsigned char n[] = "";
printf("Enter your hexadecimal string: ");
scanf("%c\n", n);
intvalue = 0;
length = strlen(n);
return EXIT_SUCCESS;
}
I am getting error messages saying
expected ';' in 'for' statement specifier
and how const char* converts between pointers and integers.
Much of OP's code is the right track, yet various coding errors exist.
Add test condition
// for(i = (length-1), j = 0; i --, j++)
// v
for(i = (length-1), j = 0; i >= 0; i --, j++)
cannot call any C library function, except strlen()
// intvalue += digit*pow(16,j);
intvalue += digit*(1u << (4*j));
Return the value
Requirement "... and returns the integer value"
//int intvalue = 0;
//...
//printf("int value is %d\n", intvalue);
//return 0;
unsigned intvalue = 0;
...
printf("integer value is %u\n", intvalue);
return intvalue;
Buffer too small
// unsigned char n[] = "";
// scanf("%c\n", n);
char n[100] = "";
scanf("%99s", n);
hexStringTouint()
Function not called in main().
Other issues exist
A simpler approach
unsigned hexStringTouint2(const char *str) {
unsigned value = 0;
while (*str) {
unsigned digit = hexCharTouint(*str); // tbd code
value = value*16 + digit;
str++;
}
return value;
}
im just came into the question with dynamic type of variable (not really dynamic, but should be determind in the runtime), the situation is like this:
i have a function which accept a double array convert it into the integer and write to a file, the integer can have different Bitlength, like 8, 16 and 32. As it is a array, i want to use a pointer to access the final result (array). So i use the void pointer with malloc and switch case now, but it will be needed to add switch case every where when i trying to access or modify this array, my question is, is there a better way to do this?
current code is like:
void foo(double * arr, int len, int iBits, FILE *fh)
{
void * newArr;
int iBytePerElement, iBase,i;
iBytePerElement = iBits / 8;
iBase = (1 << (iBits - 1)) - 1;
switch (iBytePerElement)
{
case 1:
{
newArr = (int8_t *) malloc(sizeof(int8_t)*len);
break;
}
case 2:
{
newArr = (int16_t *) malloc(sizeof(int16_t)*len);
break;
}
case 4:
{
newArr = (int32_t *) malloc(sizeof(int32_t)*len);
break;
}
}
for (i = 0; i < len; ++i)
{
switch (iBitPerElement)
{
case 1:
{
((int8_t *)newArr)[i] = (int8_t)(arr[i]*iBase);
break;
}
case 2:
{
((int16_t *)newArr)[i] = (int16_t)(arr[i]*iBase);
break;
}
case 4:
{
((int32_t *)newArr)[i] = (int32_t)(arr[i]*iBase);
break;
}
}
}
fwrite(newArr, iBytePerElement, iBytePerElement*len,fh);
}
Making a few assumptions about what you probably meant where your example code is undefined (commented), here's how I might do it:
#include <stdint.h>
#include <stdlib.h>
#include <limits.h>
void *foo(double * arr, int len, int iBits) //should return the malloced array not void
{
void * newArr;
int iBytePerElement, iBase, i;
iBytePerElement = iBits / 8;
iBase = (1 << (iBits - 1)) - 1;
if(NULL==(newArr = malloc(iBits/CHAR_BIT))) return NULL; //replace the first switch
//which obviously meant to alloc sizeof(int16_t)*len in the case 2 branch (not (sizeof(int8_t)*len) etc
for (i = 0; i < len; ++i) {
switch(iBytePerElement){
break; case 1: ((int8_t *)newArr)[i] = arr[i]*iBase;
break; case 2: ((int16_t *)newArr)[i] = arr[i]*iBase;
break; case 4: ((int32_t *)newArr)[i] = arr[i]*iBase;
}
}
return newArr;
}
Basically, you only need the second switch.
If you wanted to get rid of the 2nd switch, you could replace it with some function pointer play.
For example:
#include <stdint.h>
#include <stdlib.h>
#include <limits.h>
#define MK_FN(Bits) \
void to##Bits(void *newArr, double const*arr, int len) \
{ \
int i; for(i=0; i < len; i++) ((int##Bits##_t *)newArr)[i] = arr[i]*((1<<(Bits-1)-1)); \
}
MK_FN(8)
MK_FN(16)
MK_FN(32)
void *foo(double * arr, int len, int iBits) //should return the malloced array not void
{
void * newArr;
int iBytePerElement = iBits / 8;
if(NULL==(newArr = malloc(iBits/CHAR_BIT))) return NULL;
((void (*[])(void *,double const*, int)){ [1]=to8, [2]=to16, [4]=to32, })[iBits](newArr,arr,len);
return newArr;
}
I'm working on an embedded DSP where speed is crucial, and memory is very short.
At the moment, sprintf uses the most resources of any function in my code. I only use it to format some simple text: %d, %e, %f, %s, nothing with precision or exotic manipulations.
How can I implement a basic sprintf or printf function that would be more suitable for my usage?
This one assumes the existence of an itoa to convert an int to character representation, and an fputs to write out a string to wherever you want it to go.
The floating point output is non-conforming in at least one respect: it makes no attempt at rounding correctly, as the standard requires, so if you have have (for example) a value of 1.234 that is internally stored as 1.2399999774, it'll be printed out as 1.2399 instead of 1.2340. This saves quite a bit of work, and remains sufficient for most typical purposes.
This also supports %c and %x in addition to the conversions you asked about, but they're pretty trivial to remove if you want to get rid of them (and doing so will obviously save a little memory).
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>
static void ftoa_fixed(char *buffer, double value);
static void ftoa_sci(char *buffer, double value);
int my_vfprintf(FILE *file, char const *fmt, va_list arg) {
int int_temp;
char char_temp;
char *string_temp;
double double_temp;
char ch;
int length = 0;
char buffer[512];
while ( ch = *fmt++) {
if ( '%' == ch ) {
switch (ch = *fmt++) {
/* %% - print out a single % */
case '%':
fputc('%', file);
length++;
break;
/* %c: print out a character */
case 'c':
char_temp = va_arg(arg, int);
fputc(char_temp, file);
length++;
break;
/* %s: print out a string */
case 's':
string_temp = va_arg(arg, char *);
fputs(string_temp, file);
length += strlen(string_temp);
break;
/* %d: print out an int */
case 'd':
int_temp = va_arg(arg, int);
itoa(int_temp, buffer, 10);
fputs(buffer, file);
length += strlen(buffer);
break;
/* %x: print out an int in hex */
case 'x':
int_temp = va_arg(arg, int);
itoa(int_temp, buffer, 16);
fputs(buffer, file);
length += strlen(buffer);
break;
case 'f':
double_temp = va_arg(arg, double);
ftoa_fixed(buffer, double_temp);
fputs(buffer, file);
length += strlen(buffer);
break;
case 'e':
double_temp = va_arg(arg, double);
ftoa_sci(buffer, double_temp);
fputs(buffer, file);
length += strlen(buffer);
break;
}
}
else {
putc(ch, file);
length++;
}
}
return length;
}
int normalize(double *val) {
int exponent = 0;
double value = *val;
while (value >= 1.0) {
value /= 10.0;
++exponent;
}
while (value < 0.1) {
value *= 10.0;
--exponent;
}
*val = value;
return exponent;
}
static void ftoa_fixed(char *buffer, double value) {
/* carry out a fixed conversion of a double value to a string, with a precision of 5 decimal digits.
* Values with absolute values less than 0.000001 are rounded to 0.0
* Note: this blindly assumes that the buffer will be large enough to hold the largest possible result.
* The largest value we expect is an IEEE 754 double precision real, with maximum magnitude of approximately
* e+308. The C standard requires an implementation to allow a single conversion to produce up to 512
* characters, so that's what we really expect as the buffer size.
*/
int exponent = 0;
int places = 0;
static const int width = 4;
if (value == 0.0) {
buffer[0] = '0';
buffer[1] = '\0';
return;
}
if (value < 0.0) {
*buffer++ = '-';
value = -value;
}
exponent = normalize(&value);
while (exponent > 0) {
int digit = value * 10;
*buffer++ = digit + '0';
value = value * 10 - digit;
++places;
--exponent;
}
if (places == 0)
*buffer++ = '0';
*buffer++ = '.';
while (exponent < 0 && places < width) {
*buffer++ = '0';
--exponent;
++places;
}
while (places < width) {
int digit = value * 10.0;
*buffer++ = digit + '0';
value = value * 10.0 - digit;
++places;
}
*buffer = '\0';
}
void ftoa_sci(char *buffer, double value) {
int exponent = 0;
int places = 0;
static const int width = 4;
if (value == 0.0) {
buffer[0] = '0';
buffer[1] = '\0';
return;
}
if (value < 0.0) {
*buffer++ = '-';
value = -value;
}
exponent = normalize(&value);
int digit = value * 10.0;
*buffer++ = digit + '0';
value = value * 10.0 - digit;
--exponent;
*buffer++ = '.';
for (int i = 0; i < width; i++) {
int digit = value * 10.0;
*buffer++ = digit + '0';
value = value * 10.0 - digit;
}
*buffer++ = 'e';
itoa(exponent, buffer, 10);
}
int my_printf(char const *fmt, ...) {
va_list arg;
int length;
va_start(arg, fmt);
length = my_vfprintf(stdout, fmt, arg);
va_end(arg);
return length;
}
int my_fprintf(FILE *file, char const *fmt, ...) {
va_list arg;
int length;
va_start(arg, fmt);
length = my_vfprintf(file, fmt, arg);
va_end(arg);
return length;
}
#ifdef TEST
int main() {
float floats[] = { 0.0, 1.234e-10, 1.234e+10, -1.234e-10, -1.234e-10 };
my_printf("%s, %d, %x\n", "Some string", 1, 0x1234);
for (int i = 0; i < sizeof(floats) / sizeof(floats[0]); i++)
my_printf("%f, %e\n", floats[i], floats[i]);
return 0;
}
#endif
I wrote nanoprintf in an attempt to find a balance between tiny binary size and having good feature coverage. As of today the "bare-bones" configuration is < 800 bytes of binary code, and the "maximal" configuration including float parsing is < 2500 bytes. 100% C99 code, no external dependencies, one header file.
https://github.com/charlesnicholson/nanoprintf
I haven't seen a smaller vsnprintf implementation than this that has a comparable feature set. I also released the software in the public domain and with the Zero-clause BSD license so it's fully unencumbered.
Here's an example that uses the vsnprintf functionality:
your_project_nanoprintf.c
#define NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS 1
#define NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS 1
#define NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS 1
#define NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS 1
#define NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS 0
// Compile nanoprintf in this translation unit.
#define NANOPRINTF_IMPLEMENTATION
#include "nanoprintf.h"
your_log.h
void your_log(char const *s);
void your_log_v(char const *fmt, ...);
your_log.c
#include "your_log.h"
#include "nanoprintf.h"
#include <stdarg.h>
void your_log_v(char const *s) {
// Do whatever you want with the fully formatted string s.
}
void your_log(char const *fmt, ...) {
char buf[128];
va_arg args;
va_start(args, fmt);
npf_vsnprintf(buf, sizeof(buf), fmt, args); // Use nanoprintf for formatting.
va_end(args);
your_log_write(buf);
}
Nanoprintf also provides an snprintf-alike and a custom version that takes a user-provided putc callback for things like UART writes.
I add here my own implementation of (v)sprintf, but it does not provide float support (it is why I am here...).
However, it implements the specifiers c, s, d, u, x and the non standard ones b and m (binary and memory hexdump); and also the flags 0, 1-9, *, +.
#include <stdarg.h>
#include <stdint.h>
#define min(a,b) __extension__\
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
enum flag_itoa {
FILL_ZERO = 1,
PUT_PLUS = 2,
PUT_MINUS = 4,
BASE_2 = 8,
BASE_10 = 16,
};
static char * sitoa(char * buf, unsigned int num, int width, enum flag_itoa flags)
{
unsigned int base;
if (flags & BASE_2)
base = 2;
else if (flags & BASE_10)
base = 10;
else
base = 16;
char tmp[32];
char *p = tmp;
do {
int rem = num % base;
*p++ = (rem <= 9) ? (rem + '0') : (rem + 'a' - 0xA);
} while ((num /= base));
width -= p - tmp;
char fill = (flags & FILL_ZERO)? '0' : ' ';
while (0 <= --width) {
*(buf++) = fill;
}
if (flags & PUT_MINUS)
*(buf++) = '-';
else if (flags & PUT_PLUS)
*(buf++) = '+';
do
*(buf++) = *(--p);
while (tmp < p);
return buf;
}
int my_vsprintf(char * buf, const char * fmt, va_list va)
{
char c;
const char *save = buf;
while ((c = *fmt++)) {
int width = 0;
enum flag_itoa flags = 0;
if (c != '%') {
*(buf++) = c;
continue;
}
redo_spec:
c = *fmt++;
switch (c) {
case '%':
*(buf++) = c;
break;
case 'c':;
*(buf++) = va_arg(va, int);
break;
case 'd':;
int num = va_arg(va, int);
if (num < 0) {
num = -num;
flags |= PUT_MINUS;
}
buf = sitoa(buf, num, width, flags | BASE_10);
break;
case 'u':
buf = sitoa(buf, va_arg(va, unsigned int), width, flags | BASE_10);
break;
case 'x':
buf = sitoa(buf, va_arg(va, unsigned int), width, flags);
break;
case 'b':
buf = sitoa(buf, va_arg(va, unsigned int), width, flags | BASE_2);
break;
case 's':;
const char *p = va_arg(va, const char *);
if (p) {
while (*p)
*(buf++) = *(p++);
}
break;
case 'm':;
const uint8_t *m = va_arg(va, const uint8_t *);
width = min(width, 64); // buffer limited to 256!
if (m)
for (;;) {
buf = sitoa(buf, *(m++), 2, FILL_ZERO);
if (--width <= 0)
break;
*(buf++) = ':';
}
break;
case '0':
if (!width)
flags |= FILL_ZERO;
// fall through
case '1'...'9':
width = width * 10 + c - '0';
goto redo_spec;
case '*':
width = va_arg(va, unsigned int);
goto redo_spec;
case '+':
flags |= PUT_PLUS;
goto redo_spec;
case '\0':
default:
*(buf++) = '?';
}
width = 0;
}
*buf = '\0';
return buf - save;
}
int my_sprintf(char * buf, const char * fmt, ...)
{
va_list va;
va_start(va,fmt);
int ret = my_vsprintf(buf, fmt, va);
va_end(va);
return ret;
}
#if TEST
int main(int argc, char *argv[])
{
char b[256], *p = b;
my_sprintf(b, "%x %d %b\n", 123, 123, 123);
while (*p)
putchar(*p++);
}
#endif
tl;dr : Considering a smaller, but more complete, sprintf() implementation
https://github.com/eyalroz/printf
The standard library's sprintf() implementation you may be using is probably quite resource-taxing. But it's possible that you could avail yourself of a stand-alone sprintf() implementation, you would get more complete functionality without paying with so much memory use.
Now, why would you choose that if you've told us you only need some basic functionality? Because the nature of (s)printf() use is that we tend to use more aspects of it as we go along. You notice you want to print larger numbers, or differences in far decimal digits; you want to print a bunch of values and then decide you want them aligned. Or somebody else wants to use the printing capability you added to print something you haven't thought of. So, instead of having to switch implementations, you use an implementation where compile-time options configure which features get compiled and which get left out.