According to a top answer on SO this is what I should do to convert an integer to a string in C:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <math.h>
#define digits(x) ((int)((ceil(log10(abs(x)))+1)*sizeof(char)))
int main () {
int a = 345908220;
long b = 23094809284358;
unsigned int c = 3456789234;
uint8_t d = 242;
int64_t e = -840958202029834;
int dstr_len = digits(a) + digits(b) + digits(c) +
digits(d) + digits(e) + 5;
char dstr[dstr_len];
sprintf(dstr, "%d %ld %u %u %" PRIu64, a, b, c, d, e);
printf("%s\n", dstr);
return 0;
}
However, this seems ridiculously inefficient. I have to link my program to libmath, and make three math calls for every integer that I want to print. Also note that I had to add 5 to my buffer and not just 1 for the NUL terminator, by counting the number of spaces in my format string. This also seems error-prone, and could lead to a buffer overflow.
So, is there any nice, standard function, that will compute the size of my buffer for me?
I'm trying to write secure C.
If your compiler has snprintf() available, you can request the formatted buffer length and then allocate accordingly:
int dstr_len = snprintf(NULL, 0, "%d %ld %u %u %" PRIu64, a, b, c, d, e) + 1;
char dstr[dstr_len];
//
// NOTE: variable-length arrays are NOT supported in all compilers!
// A more portable solution is:
//
// char *dstr = malloc(sizeof(char) * dstr_len);
snprintf(dstr, dstr_len, "%d %ld %u %u %" PRIu64, a, b, c, d, e);
// free(dstr);
You can use snprintf for this. From the manpage:
The functions snprintf() and vsnprintf() do not write more than size bytes (including the teriitminating null byte ('\0')). If the output was truncated due to this limit then the return
value is the number of characters (excluding the terminating null byte) which would have been
written to the final string if enough space had been available. Thus, a return value of size
or more means that the output was truncated.
Thus you can call it with 0 for the size and capture the return value then allocate based on that.
You can use asprintf, which allocates a large enough output string for you.
Don't forget to free the output string, because it is dynamically allocated.
asprintf is available on Mac OSX, Linux, and BSD. The source code is available from Apple if you wish to use it on other platforms.
Example:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
int main () {
int a = 345908220;
long b = 23094809284358;
unsigned int c = 3456789234;
uint8_t d = 242;
int64_t e = -840958202029834;
char *dstr;
asprintf(&dstr, "%d %ld %u %u %" PRIu64, a, b, c, d, e);
if (dstr == NULL) {perror(NULL), exit(1);}
printf("%s\n", dstr);
free(dstr);
return 0;
}
Most C "runtime environments" are at most 64 bits. You could test that int is at most 64 bits (with <stdint.h> and <limits.h>) then use snprintf (not sprintf which is unsafe and deprecated) on a large enough buffer (32 bytes is more than enough for 264 in decimal).
See Posix spec on limits.h which define WORD_BIT so
#if WORD_BIT > 64
#error cannot compile on this machine
#endif
char buf[32];
snprintf(buf, sizeof(buf), "%d", a);
BTW, <stdint.h> defines several types. You might want intmax_t
Related
I am trying to convert a series of decimal numbers to their hex representation in string format and then back from string to decimal. This might sound strange but is a simplified representation of a more complex situation.
So, either way, I have the following piece of code which almost works fine. For some reason my variable a is still equal to 0 at the end while it should equal 43, all the other variables seem to be alright:
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
/********************* A *******************/
uint16_t a = 43; //0x002b
int16_t b = -43; //0xffd5
uint32_t c = 234; //0x000000ea
int32_t d = -234; //0xffffff16
char aStr[10]={0};
char bStr[10]={0};
char cStr[10]={0};
char dStr[10]={0};
snprintf(aStr, sizeof(aStr), "%04hhx", a);
snprintf(bStr, sizeof(bStr), "%04x", b & 0xFFFF);
snprintf(cStr, sizeof(cStr), "%08hhx", c);
snprintf(dStr, sizeof(aStr), "%08x", d & 0xFFFFFFFF);
fprintf(stdout, "TX a = %s.\n", aStr);
fprintf(stdout, "TX b = %s.\n", bStr);
fprintf(stdout, "TX c = %s.\n", cStr);
fprintf(stdout, "TX d = %s.\n", dStr);
/********************* B *******************/
uint16_t aOut = 0;
int16_t bOut = 0;
uint32_t cOut = 0;
int32_t dOut = 0;
sscanf(aStr, "%04hhx", &aOut);
sscanf(bStr, "%04x", &bOut);
sscanf(cStr, "%08hhx", &cOut);
sscanf(dStr, "%08x", &dOut);
fprintf(stdout, "rx a = %d\n", aOut); //<---- this line prints 0 for a. Why?
fprintf(stdout, "rx b = %d\n", bOut);
fprintf(stdout, "rx c = %d\n", cOut);
fprintf(stdout, "rx d = %d\n", dOut);
return 0;
}
Does anybody know why or what I am missing?
The line
sscanf(aStr, "%04hhx", &aOut);
is wrong. The %hhx conversion format specifier requires an argument of type unsigned char *, but you are instead passing it an argument of type uint16_t *. This invokes undefined behavior.
I suggest that you change that line to the following:
sscanf(aStr, "%04"SCNx16, &aOut);
On most platforms, the macro SCNx16 will simply expand to "hx", but it is generally safer to use the macro, in case you code happens to be running on a (future) platform on which uint16_t is not equivalent to an unsigned short.
The lines
sscanf(bStr, "%04x", &bOut);
sscanf(cStr, "%08hhx", &cOut);
sscanf(dStr, "%08x", &dOut);
are also using the wrong conversion format specifiers. I recommend that you use the following code instead:
sscanf(bStr, "%04"SCNx16, &bOut);
sscanf(cStr, "%08"SCNx32, &cOut);
sscanf(dStr, "%08"SCNx32, &dOut);
Strictly speaking, the above code also invokes undefined behavior, because the %x conversion format specifier requires a pointer to an unsigned type instead of to a signed type. However, this should not be a problem in practice, provided that the converted value can be represented both in the signed type and in the unsigned type.
This is written in 'c' and compiled with gcc. I'm not sure what else you need to know.
The smallest complete example I could put together is shown here. The variable 'numatoms' loses its value when it gets to line 23 (after the scanf()).
I'm stumped. Maybe it has something to do with scanf() overwritting the space for numatoms?
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
/*
*
*/
int main(int argc, char** argv) {
uint8_t numatoms;
uint8_t r;
char a[20];
do {
printf("NO. OF ATOMS?");
fflush(stdout);
scanf("%d", &numatoms);
printf("\r\n");
for(;;){
printf("value of numatoms is %u\r\n", numatoms);
printf("RAY?");
fflush(stdout);
scanf("%u", &r);
printf("value of numatoms is %u\r\n", numatoms);
if(r < 1)
break;
else {
printf("value of numatoms is %u\r\n", numatoms);
}
}
printf("CARE TO TRY AGAIN?");
fflush(stdout);
scanf("%s", a);
printf("\r\n");
} while (a[0] == 'y' || a[0] == 'Y');
return (EXIT_SUCCESS);
}
uint8_t is 8 bits long %u reads an unsigned int (probably 32 bits long).
You either need to make numatoms "bigger" (i.e. unsigned int) or read the correct size (see scanf can't scan into inttypes (uint8_t))
You should use macros for format specifiers for integer types defined in the header <inttypes.h>.
From the C Standard (7.8.1 Macros for format specifiers)
1 Each of the following object-like macros expands to a character
string literal containing a conversion specifier, possibly modified by
a length modifier, suitable for use within the format argument of a
formatted input/output function when converting the corresponding
integer type. These macro names have the general form of PRI
(character string literals for the fprintf and fwprintf family) or SCN
(character string literals for the fscanf and fwscanf family),217)
followed by the conversion specifier, followed by a name corresponding
to a similar type name in 7.20.1. In these names, N represents the
width of the type as described in 7.20.1.
The general form of the macro used for unsigned integer types with the conversion specifier u looks like
SCNuN
Here is a demonstrative program
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main(void)
{
uint8_t x;
scanf( "%" SCNu8, &x );
printf( "x = %u\n", x );
return 0;
}
I need to convert uint16_t value to a string. I want the string to be a decimal respresentation of the number.
Example: uint16_t i=256 string: 256
I tried with itoa(i,string, 10) but when i value increases starts printing negative values.
I send the string via the Serial Port.(UART)
It is there some alternative?
Use sprintf with %u format for unsigned int:
uint16_t i = 33000;
sprintf(str, "%u", i);
You can try to use sprintf() for common "to string" conversions. For example:
#include <stdio.h>
#include <math.h>
int main() {
uint16_t i = 256;
char str[80];
int str_len = sprintf(str, "%d", i);
return(0);
}
For more info look at this article.
I want to store a long value (LONG_MAX in my test program) in a dynamically allocated string, but I'm confused how much memory I need to allocate for the number to be displayed in the string.
My fist attempt:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
int main(void)
{
char *format = "Room %lu somedata\n";
char *description = malloc(sizeof(char) * strlen(format) + 1);
sprintf(description, format, LONG_MAX);
puts(description);
return 0;
}
Compiled with
gcc test.c
And then running it (and piping it into hexdump):
./a.out | hd
Returns
00000000 52 6f 6f 6d 20 39 32 32 33 33 37 32 30 33 36 38 |Room 92233720368|
00000010 35 34 37 37 35 38 30 37 20 62 6c 61 62 6c 61 0a |54775807 blabla.|
00000020 0a |.|
00000021
Looking at the output, it seems my memory allocation of sizeof(char) * strlen(format) + 1 is wrong (too less memory allocated) and it works more accidentally?
What is the correct amount to allocate then?
My next idea was (pseudo-code):
sizeof(char) * strlen(format) + strlen(LONG_MAX) + 1
This seems too complicated and pretty non-idomatic. Or am I doing something totally wrong?
You are doing it totally wrong. LONG_MAX is an integer, so you can't call strlen (). And it's not the number that gives the longest result, LONG_MIN is. Because it prints a minus character as well.
A nice method is to write a function
char* mallocprintf (...)
which has the same arguments as printf and returns a string allocated using malloc with the exactly right length. How you do this: First figure out what a va_list is and how to use it. Then figure out how to use vsnprintf to find out how long the result of printf would be without actually printing. Then you call malloc, and call vsnprintf again to produce the string.
This has the big advantage that it works when you print strings using %s, or strings using %s with some large field length. Guess how many characters %999999d prints.
You can use snprintf() to figure out the length without worrying about the size of LONG_MAX.
When you call snprintf with NULL string, it'll return a number of bytes that would have been required if it was write into the buffer and then you know exactly how many bytes are required.
char *format = "Room %lu somedata\n";
int len = snprintf(0, 0, format, LONG_MAX); // Returns the number of
//bytes that would have been required for writing.
char *description = malloc( len+1 );
if(!description)
{
/* error handling */
}
snprintf(description, len+1, format, LON_MAX);
Convert the predefined constant numeric value to a string, using macro expansion as explaned in convert digital to string in macro:
#define STRINGIZER_(exp) #exp
#define STRINGIZER(exp) STRINGIZER_(exp)
(code courtesy of Whozcraig). Then you can use
int max_digit = strlen(STRINGIZER(LONG_MAX))+1;
or
int max_digit = strlen(STRINGIZER(LONG_MIN));
for signed values, and
int max_digit = strlen(STRINGIZER(ULONG_MAX));
for unsigned values.
Since the value of LONG_MAX is a compile-time, not a run-time value, you are ensured this writes the correct constant for your compiler into the executable.
To allocate enough room, consider worst case
// Over approximate log10(pow(2,bit_width))
#define MAX_STR_INT(type) (sizeof(type)*CHAR_BIT/3 + 3)
char *format = "Room %lu somedata\n";
size_t n = strlen(format) + MAX_STR_INT(unsigned long) + 1;
char *description = malloc(n);
sprintf(description, format, LONG_MAX);
Pedantic code would consider potential other locales
snprintf(description, n, format, LONG_MAX);
Yet in the end, recommend a 2x buffer
char *description = malloc(n*2);
sprintf(description, format, LONG_MAX);
Note: printing with specifier "%lu" ,meant for unsigned long and passing a long LONG_MAX in undefined behavior. Suggest ULONG_MAX
sprintf(description, format, ULONG_MAX);
With credit to the answer by #Jongware, I believe the ultimate way to do this is the following:
#define STRINGIZER_(exp) #exp
#define STRINGIZER(exp) STRINGIZER_(exp)
const size_t LENGTH = sizeof(STRINGIZER(LONG_MAX)) - 1;
The string conversion turns it into a string literal and therefore appends a null termination, therefore -1.
And not that since everything is compile-time constants, you could as well simply declare the string as
const char *format = "Room " STRINGIZER(LONG_MAX) " somedata\n";
You cannot use the format. You need to observer
LONG_MAX = 2147483647 = 10 characters
"Room somedata\n" = 15 characters
Add the Null = 26 characters
so use
malloc(26)
should suffice.
You have to allocate a number of char equal to the digits of the number LONG_MAX that is 2147483647. The you have to allocate 10 digit more.
in your format string you fave
Room = 4 chars
somedata\n = 9
spaces = 2
null termination = 1
The you have to malloc 26 chars
If you want to determinate runtime how man digit your number has you have to write a function that test the number digit by digit:
while(n!=0)
{
n/=10; /* n=n/10 */
++count;
}
Another way is to store temporary the sprintf result in a local buffer and the mallocate strlen(tempStr)+1 chars.
Usually this is done by formatting into a "known" large enough buffer on the stack and then dynamically allocated whatever is needed to fit the formatted string. i.e.:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
int main(void)
{
char buffer[1024];
sprintf(buffer, "Room %lu somedata\n", LONG_MAX);
char *description = malloc( strlen( buffer ) + 1 );
strcpy( description, buffer );
puts(description);
return 0;
}
Here, your using strlen(format) for allocation of memory is bit problematic. it will allocate memory considering the %lu lexicographically, not based on the lexicographical width of the value that can be printed with %lu.
You should consider the max possible value for unsigned long,
ULONG_MAX 4294967295
lexicographically 10 chars.
So, you've to allocate space for
The actual string (containing chars), plus
10 chars (at max), for the lexicographical value for %lu , plus
1 char to represnt - sign, in case the value is negative, plus
1 null terminator.
Well, if long is a 32-bit on your machine, then LONG_MAX should be 2147483647, which is 10 characters long. You need to account for that, the rest of your string, and the null character.
Keep in mind that long is a signed value with maximum value of LONG_MAX, and you are using %lu (which is supposed to print an unsigned long value). If you can pass a signed value to this function, then add an additional character for the minus sign, otherwise you might use ULONG_MAX to make it clearer what your limits are.
If you are unsure which architecture you are running on, you might use something like:
// this should work for signed 32 or 64 bit values
#define NUM_CHARS ((sizeof(long) == 8) ? 21 : 11)
Or, play safe and simply use 21. :)
Use the following code to calculate the number of characters necessary to hold the decimal representation of any positve integer:
#include <math.h>
...
size_t characters_needed_decimal(long long unsigned int llu)
{
size_t s = 1;
if (0LL != llu)
{
s += log10(llu);
}
return s;
}
Mind to add 1 when using a C-"string" to store the number, as C-"string"s are 0-terminated.
Use it like this:
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
size_t characters_needed_decimal(long long unsigned int);
int main(void)
{
size_t s = characters_needed_decimal(LONG_MAX);
++s; /* 1+ as we want a sign */
char * p = malloc(s + 1); /* add one for the 0-termination */
if (NULL == p)
{
perror("malloc() failed");
exit(EXIT_FAILURE);
}
sprintf(p, "%ld", LONG_MAX);
printf("LONG_MAX = %s\n", p);
sprintf(p, "%ld", LONG_MIN);
printf("LONG_MIN = %s\n", p);
free(p);
return EXIT_SUCCESS;
}
Safest:
Rather than predict the allocation needed, uses asprintf(). This function allocates memory as needed.
char *description = NULL;
asprintf(&description, "Room %lu somedata\n", LONG_MAX);
asprintf() is not standard C, but is common in *nix and its source code is available to accommodate other systems.
Why Use Asprintf?
apple
android
I have the following piece of code compiling under gcc:
int parseMsg(const char *msg_to_parse, unsigned long *exp_input, unsigned long *sysTicks )
{
int l_msg_size = strlen(msg_to_parse);
if(l_msg_size <10)
return -1;
char l_exp_input_arr[10];
char l_sys_ticks_arr[10];
memcpy(l_sys_ticks_arr,msg_to_parse+12,10);
memcpy(l_exp_input_arr,msg_to_parse,10);
//l_msg_size = strlen(msg_to_parse);
*sysTicks = strtoul(l_sys_ticks_arr,NULL,10);
*exp_input = strtoul(l_exp_input_arr,NULL,10);
return 0;
}
And I'm trying to test it in the following manner:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int parseMsg(const char *msg_to_parse, unsigned long *exp_input, unsigned long *sysTicks );
int main(void) {
char msg[] = "1234567890 59876543213";
unsigned long along1, along2;
along1 =0;
along2=0;
parseMsg(msg,&along1, &along2 );
printf("result of parsing: \n \t Along 1 is %lu \n \t Along 2 is %lu \n",along1, along2);
return 0;
}
But, I'm getting the following result:
result of parsing:
Along 1 is 1234567890
Along 2 is 4294967295
Why is the second unsigned long wrong?
Thanks
The second integer you provide is too big to be represented in memory on your architecture. So, according to its API, strtoul is just returning you ULONG_MAX (=4294967295 on your architecture), along with setting errno to ERANGE
strtoul API is here : http://www.cplusplus.com/reference/clibrary/cstdlib/strtoul/
BUT it may also fail if you gave a smaller integer, because strtoul only stops parsing when it encounters a non-numerical character. Since you didn't ensure that, you cannot be sure that strtoul will not try to parse whatever is in memory just after your strings. (So assuming random, you have 10 chance out of 256 to have a conversion error)
Terminate your strings with \0, it will be ok then :
char l_exp_input_arr[11]; // +1 for \0
char l_sys_ticks_arr[11];
memcpy(l_sys_ticks_arr, msg_to_parse+12, 10);
l_sys_ticks_arr[10] = '\0';
memcpy(l_exp_input_arr, msg_to_parse, 10);
l_exp_input_arr[10] = '\0';
You need to make your two temporary char[] variables one char longer and then make the last character NULL.