Using sprintf to convert an unsigned long to char array producing 0 - c

I am trying to convert an unsigned long using the sprintf function in c. Code goes like:
char ID[6];
sprintf(ID,"%lu",a.id);
a.id is a number that is passed in that can range from 0 > but I only want the first 6 regardless. Using printf("%lu",a.id); prior to conversion prints the right number but once I try print the string from the char the outcome is 0. Not too sure why this is happening any advice would be much appreciated.

... once I try print the string from the char the outcome is 0
Code is risking undefined behavior (UB): buffer overrun, potential wrong specifier.
but I only want the first 6 regardless.
Insure the buffer is big enough for 6 characters and a terminating null character.
//char ID[6];
char ID[6+1];
Handle a.id outside the expected range of 0...999999 with % some_unsigned_constant. This does print the last 6.
// sprintf(ID,"%lu",a.id);
sprintf(ID,"%lu",a.id % 1000000u);
As type of a.id, not posted, 2 steps may be useful to make sure a matching print specifier is used.
// sprintf(ID,"%lu",a.id % 1000000u);
unsigned long ul = a.id
sprintf(ID,"%lu", ul % 1000000u);
To print the first six, even if outside the 0...999999 range, use snprintf() which will print only up to the first 6.
char ID[6+1];
snprintf(ID, sizeof ID, "%lu", (unsigned long) a.id);

And the correct way to do what the OP wants is to
#include <stdio.h>
int main()
{
char ID[6];
unsigned long x = 3453342432;
int r = snprintf(ID, sizeof(ID), "%lu",x);
printf("%d %s\n", r, ID);
return 0;
}
Note that the snprintf result is a number of characters that would had been written. It was noted by others that you must reserve one character extra space for terminating NULL.

Related

Function is returning a different value every time?

I'm trying to convert a hexadecimal INT to a char so I could convert it into a binary to count the number of ones in it. Here's my function to convert it into char:
#include <stdio.h>
#include <stdlib.h>
#define shift(a) a=a<<5
#define parity_even(a) a = a+0x11
#define add_msb(a) a = a + 8000
void count_ones(int hex){
char *s = malloc(2);
sprintf(s, "0x%x", hex);
free(s);
printf("%x", s);
};
int main() {
int a = 0x01B9;
shift(a);
parity_even(a);
count_ones(a);
return 0;
}
Every time I run this, i always get different outputs but the first three hex number are always the same. Example of outputs:
8c0ba2a0
fc3b92a0
4500a2a0
d27e82a0
c15d62a0
What exactly is happening here? I allocated 2 bytes for the char since my hex int is 2 bytes.
It's too long to write a comment so here goes:
I'm trying to convert a hexadecimal INT
int are stored as a group of value, padding (possible empty) and sign bits, so is there no such thing as a hexadecimal INT but you can represent (print) a given number in the hexadecimal format.
convert a ... INT to a char
That would be lossy conversion as an int might have 4 bytes of data that you are trying to cram into a 1 byte. char specifically may be signed or unsigned. You probably mean string (generic term) or char [] (standard way to represent a string in C).
binary to count the number of ones
That's the real issue you are trying to solve and this is a duplicate of:
How to count the number of set bits in a 32-bit integer?
count number of ones in a given integer using only << >> + | & ^ ~ ! =
To address the question you ask:
Need to allocate more than 2 bytes. Specifically ceil(log16(hex)) + 2 (for 0x) + 1 (for trailing '\0').
One way to get the size is to just ask snprintf(s, 0, ...)
then allocate a suitable array via malloc (see first implementation below) or use stack allocated variable length array (VLA).
You can use INT_MAX instead of hex to get an upper
bound. log16(INT_MAX) <= CHAR_BIT * sizeof(int) / 4 and the
latter is a compile time constant. This means you can allocate your string on stack (see 2nd implementation below).
It's undefined behavior to use a variable after it's deallocated. Move free() to after the last use.
Here is one of the dynamic versions mentioned above:
void count_ones(unsigned hex) {
char *s = NULL;
size_t n = snprintf(s, 0, "0x%x", hex) + 1;
s = malloc(n);
if(!s) return; // memory could not be allocated
snprintf(s, n, "0x%x", hex);
printf("%s (size = %zu)", s, n);
free(s);
};
Note, I initialized s to NULL which would cause the first call to snprintf() to return an undefined value on SUSv2 (legacy). It's well defined on c99 and later. The output is:
0x3731 (size = 7)
And the compile-time version using a fixed upper bound:
#include <limits.h>
// compile-time
void count_ones(unsigned hex) {
char s[BIT_CHAR * sizeof(int) / 4 + 3];
sprintf(s, "0x%x", hex);
printf("%s (size = %zu)", s, n);
};
and the output is:
0x3731 (size = 11)
Your biggest problem is that malloc isn't allocating enough. As Barmar said, you need at least 7 bytes to store it or you could calculate the amount needed. Another problem is that you are freeing it and then using it. It is only one line after the free that you use it again, which shouldn't have anything bad happen like 99.9% of the time, but you should always free after you know you are done using it.

What is the point of assigning the size of a string?

For an instance if I store ABCDE from scanf function, the later printf function gives me ABCDE as output. So what is the point of assigning the size of the string(Here 4).
#include <stdio.h>
int main() {
int c[4];
printf("Enter your name:");
scanf("%s",c);
printf("Your Name is:%s",c);
return 0;
}
I'll start with, don't use int array to store strings!
int c[4] allocates an array of 4 integers. An int is typically 4 bytes, so usually this would be 16 bytes (but might be 8 or 32 or something else on some platforms).
Then, you use this allocation first to read characters with scanf. If you enter ABCDE, it uses up 6 characters (there is an extra 0 byte at the end of the string marking the end, which needs space too), which happens to fit into the memory reserved for array of 4 integers. Now you could be really unlucky and have a platform where int has a so called "trap representation", which would cause your program to crash. But, if you are not writing the code for some very exotic device, there won't be. Now it just so happens, that this code is going to work, for the same reason memcpy is going to work: char type is special in C, and allows copying bytes to and from different types.
Same special treatment happens, when you print the int[4] array with printf using %s format. It works, because char is special.
This also demonstrates how very unsafe scanf and printf are. They happily accept c you give them, and assume it is a char array with valid size and data.
But, don't do this. If you want to store a string, use char array. Correct code for this would be:
#include <stdio.h>
int main() {
char c[16]; // fits 15 characters plus terminating 0
printf("Enter your name:");
int items = scanf("%15s",c); // note: added maximum characters
// scanf returns number of items read successfully, *always* check that!
if (items != 1) {
return 1; // exit with error, maybe add printing error message
}
printf("Your Name is: %s\n",c); // note added newline, just as an example
return 0;
}
The size of an array must be defined while declaring a C String variable because it is used to calculate how many characters are going to be stored inside the string variable and thus how much memory will be reserved for your string. If you exceed that amount the result is undefined behavior.
You have used int c , not char c . In C, a char is only 1 byte long, while a int is 4 bytes. That's why you didn't face any issues.
(Simplifying a fair amount)
When you initialize that array of length 4, C goes and finds a free spot in memory that has enough consecutive space to store 4 integers. But if you try to set c[4] to something, C will write that thing in the memory just after your array. Who knows what’s there? That might not be free, so you might be overwriting something important (generally bad). Also, if you do some stuff, and then come back, something else might’ve used that memory slot (properly) and overwritten your data, replacing it with bizarre, unrelated, and useless (to you) data.
In C language the last of the string is '\0'.
If you print with the below function, you can see the last character of the string.
scanf("%s", c); add the last character, '\0'.
So, if you use another function, getc, getch .., you should consider adding the laster character by yourself.
#include<stdio.h>
#include<string.h>
int main(){
char c[4+1]; // You should add +1 for the '\0' character.
char *p;
int len;
printf("Enter your name:");
scanf("%s", c);
len = strlen(c);
printf("Your Name is:%s (%d)\n", c, len);
p = c;
do {
printf("%x\n", *(p++));
} while((len--)+1);
return 0;
}
Enter your name:1234
Your Name is:1234 (4)
31
32
33
34
0 --> last character added by scanf("%s);
ffffffae --> garbage

How do I subtract 1 from an array of integers

I am learning C in school, and I am having a little difficulty on my project.
Basically, I am writing a function that gets a string containing a positive integer. The function subtracts 1 from that integer and puts the obtained value in the string.
So , if I have this ;
char nums[] = "2462";
how do I write a function that will subtract 1 from the integer, so that the result is "2461"??
First, convert the character array into an integer.
You can use atoi (ASCII to Integer), but because it returns 0 on error there's no way to tell the difference between successfully converting "0" and an error.
Instead use strtol (STRing TO Long integer).
// end stores where parsing stopped.
char *end;
// Convert nums as a base 10 integer.
// Store where the parsing stopped in end.
long as_long = strtol(nums, &end, 10);
// If parsing failed, end will point to the start of the string.
if (nums == end) {
perror("Parsing nums failed");
}
Now you can subtract, turn the integer back into a string with sprintf, and put it in nums.
sprintf(nums, "%ld", as_long - 1);
This is not entirely safe. Consider if nums is "0". It only has 1 byte of space. If we subtract 1 then we have "-1" and we're storing 2 characters where we only have memory for 1.
For the full explanation of how to do this safely, see How to convert an int to string in C?.
Alternatively, don't store it, just print it.
printf("%ld", as_long - 1);
One way is to convert string -> int -> string.
You can do this by using atoi and sprintf.
Simple implementation (far from perfect):
#include <stdlib.h>
#include <stdio.h>
int main()
{
int a;
char b[5];
a = atoi("2462");
a--;
sprintf(b, "%d", a);
printf("%s\n", b);
return 1;
}

understanding ATOI function

Hi I have a text file which contains the below data
ABC00011234567
XYZ00021234567
To get the data, i have defined a structure
typedef struct data {
char x[3];
char y[4];
char z[7];
} key;
in the program what I do is read each line and assign it to the structure
unsigned char buf[1024];
fgets(buf,sizeof(buf),fptr);
key *k=(key*)buf;
int y = atoi(k->y)
printf( "y=%d\n",y);`
I'm getting the output as
y=1123456
y=2123456
the output Im expecting is
y=1
y=2
should I assume, atoi takes the pointer of the string and iterates till EOF is encountered?
what should I do to get the values 1 and 2?
atoi takes a nul-terminated string. You'll have to add your own terminators to your key members if you want to limit the length of data atoi parses
You should assume that atoi() keeps going until it reaches the end of the string or an invalid character. For example, for the string `"123zzz" it'd return 123.
You should either terminate your strings (put a zero at the end of them) and stop using atoi() (e.g. use strtol() instead); or write your own conversion that doesn't need a terminated string.
Note: (in general) atoi() should never be used for anything other than writing a compiler, because it does things that don't make sense to normal people (e.g. "0129" is 10 and not 129 because it decides the number is octal and the 9 isn't a valid digit for octal).
The atoi function expects a null-terminated string; you are passing a portion of the char array that has its termination past the boundaries of key::y, so atoi interprets the entire value as a number. If you would like to stick to your "cookie cutter" method of parsing the key, you need to make a copy, and pass it to atoi:
char temp[5];
memcpy(temp, k->y, 4);
temp[4] = '\0';
int y = atoi(temp);
However, I think that using fscanf is a better choice:
char x[4];
int y, z;
fscanf(fptr, "%3s%4d%7d", x, &y, &z);
printf("%s %d %d", x, y, z);
atoi() assumes a zero terminated string. In your case, the string will not be zero-terminated, and thus the data in z is read by atoi after y has been read.
To read just the 4 digits, you can use sscanf:
sscanf(k->y,"%4d",&y);

Getting gibberish instead of numbers using memcpy and strtoul

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.

Resources