Related
I would like to convert a very long (arbitrary length, possibly 1000 characters long; university assignment) string into binary. How should I approach this problem? I have thought about it for a while, but I just can't seem to think of anything viable.
The string will be passed to me as const char *str. I want to read the number, which will be in Base 10, and convert it into binary.
Should I read certain number of least significant numbers and store them in unsigned long long int, and then work from there? Is there a better solution? I don't know how the method I suggested would pan out. I wanted to know if there's a better/easier way to do it.
Thank you.
Assuming your input is too large for the biggest integer type, you have to convert it to a unlimited size integer. For this purpose you can use gmplib. If you are not allowed to use external libraries, you can use a different approach:
is your string divisible by two (look at the last digit)?
if yes, write 0 to left side of your output
else, write 1 to left side of your output
divide the string by 2 (every digit)
repeat while string is not filled with 0
I am going to edit this answer, as soon as I wrote the code.
Here you go:
#include<stdbool.h>
#include<stdlib.h>
#include<memory.h>
#include<stdio.h>
typedef struct char_queue {
unsigned int len;
unsigned int capacity;
char* data;
} char_queue;
char_queue init_char_queue() {
return (char_queue) {
0,
4096,
malloc(4096)
};
}
void enqueue(char_queue* queue, char val) {
if (queue->len == queue->capacity) {
char* new_queue_data = malloc(queue->capacity + 4096);
memmove(new_queue_data, queue->data, queue->capacity);
free(queue->data);
queue->data = new_queue_data;
}
queue->len++;
queue->data[queue->capacity - queue->len] = val;
}
char* queue_get_arr(char_queue* queue) {
char* output = malloc(queue->len);
memcpy(output, &queue->data[queue->capacity - queue->len], queue->len);
return output;
}
void free_char_queue(char_queue* queue) {
if (queue->data) free(queue->data);
}
void convert_to_digit_arr(char* input, unsigned int len) {
for (unsigned int i = 0; i < len; i++) {
input[i] = input[i] - '0'; // '5' - '0' = 5
}
}
bool is_null(char* input, unsigned int len) {
for (unsigned int i = 0; i < len; i++) {
if (input[i] != 0) return false;
}
return true;
}
bool divisible_by_two(char* digit_arr, unsigned int len) {
return digit_arr[len - 1] % 2 == 0;
}
void divide_by_two(char* digit_arr, unsigned int len) {
for (unsigned int i = 0; i < len; i++) {
bool is_odd = digit_arr[i] % 2 == 1;
digit_arr[i] /= 2;
if (is_odd && i + 1 < len) { // and is not last (right) digit
digit_arr[i + 1] += 10;
}
}
}
int main(int argc, char** argv) {
for (int i = 1; i < argc; i++) {
unsigned int input_len = strlen(argv[i]);
char* input = malloc(input_len + 1);
strcpy(input, argv[i]);
convert_to_digit_arr(input, input_len);
char_queue queue = init_char_queue();
enqueue(&queue, 0); // null terminator to use the queue content as a string
while (!is_null(input, input_len)) {
enqueue(&queue, divisible_by_two(input, input_len) ? '0' : '1');
divide_by_two(input, input_len);
}
free(input);
char* output = queue_get_arr(&queue);
printf("%s\n", output);
free(output);
free_char_queue(&queue);
}
}
This is not the fastest approach, but it is very simple. Also feel free to optimize it.
How do I convert a really long string (as decimal characters) to binary?
Let us look at printing this.
print2(s)
If the decimal string is at least "2",
__ Divide the decimal string by 2 and notice its remainder.
__ Recursively call print2(s)
__ Print the remainder.
Else print the string.
Example code:
#include <stdio.h>
unsigned decimal_string_divide(char *dividend, unsigned divisor) {
// Remove a potential leading '0'
if (*dividend == '0') {
memmove(dividend, dividend+1, strlen(dividend));
}
// "divide", like we learned in grade school.
unsigned remainder = 0;
while (*dividend) {
unsigned sum = remainder*10 + (*dividend - '0');
remainder = sum%divisor;
*dividend = sum/divisor + '0';
dividend++;
}
return remainder;
}
void decimal_string_print_binary(char *dividend) {
//printf("<%s>\n", dividend); fflush(stdout);
if (dividend[0]) {
// If at least 2 digits or at least "2"
if (dividend[1] || (dividend[0] >= '2')) {
unsigned bit = decimal_string_divide(dividend, 2);
decimal_string_print_binary(dividend);
printf("%c", bit + '0');
} else {
printf("%c", *dividend);
}
}
}
void decimal_string_print_2(const char *dividend) {
printf("%-25s", dividend);
size_t sz = strlen(dividend) + 1;
char buf[sz]; // Use a VLA or allocate memory
strcpy(buf, dividend);
decimal_string_print_binary(buf);
printf("\n");
}
Test
int main(void) {
decimal_string_print_2("0");
decimal_string_print_2("1");
decimal_string_print_2("42");
decimal_string_print_2("8675309");
decimal_string_print_2("18446744073709551615");
}
Output
0 0
1 1
42 101010
8675309 100001000101111111101101
18446744073709551615 1111111111111111111111111111111111111111111111111111111111111111
To instead convert the string from decimal form into a binary string, allocate sufficient buffer (about log10(2) times string length) and instead of printing above, save to the buffer. Left for OP to do.
I am suggesting a better approach. whereby any arguments passed to a function that is not intended to be mutated, be received as a "const", and a local pointer be used to access the data of this "const".
IE:
void to_binary(const char *str) {
char *ptr = str;
...
Then use ptr.
I know that in this case, my argument is purely trivial and academic, but it is a good practice to get used to and may save you many headaches in the future.
Also, when dealing with binary data, use "unsigned char", to ensure that no type conversions are used. You will need bit 7 if the data is not ASCII or alike.
Right now I am trying to convert an int to a char in C programming. After doing research, I found that I should be able to do it like this:
int value = 10;
char result = (char) value;
What I would like is for this to return 'A' (and for 0-9 to return '0'-'9') but this returns a new line character I think.
My whole function looks like this:
char int2char (int radix, int value) {
if (value < 0 || value >= radix) {
return '?';
}
char result = (char) value;
return result;
}
to convert int to char you do not have to do anything
char x;
int y;
/* do something */
x = y;
only one int to char value as the printable (usually ASCII) digit like in your example:
const char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int inttochar(int val, int base)
{
return digits[val % base];
}
if you want to convert to the string (char *) then you need to use any of the stansdard functions like sprintf, itoa, ltoa, utoa, ultoa .... or write one yourself:
char *reverse(char *str);
const char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char *convert(int number, char *buff, int base)
{
char *result = (buff == NULL || base > strlen(digits) || base < 2) ? NULL : buff;
char sign = 0;
if (number < 0)
{
sign = '-';
}
if (result != NULL)
{
do
{
*buff++ = digits[abs(number % (base ))];
number /= base;
} while (number);
if(sign) *buff++ = sign;
if (!*result) *buff++ = '0';
*buff = 0;
reverse(result);
}
return result;
}
A portable way of doing this would be to define a
const char* foo = "0123456789ABC...";
where ... are the rest of the characters that you want to consider.
Then and foo[value] will evaluate to a particular char. For example foo[0] will be '0', and foo[10] will be 'A'.
If you assume a particular encoding (such as the common but by no means ubiquitous ASCII) then your code is not strictly portable.
Characters use an encoding (typically ASCII) to map numbers to a particular character. The codes for the characters '0' to '9' are consecutive, so for values less than 10 you add the value to the character constant '0'. For values 10 or more, you add the value minus 10 to the character constant 'A':
char result;
if (value >= 10) {
result = 'A' + value - 10;
} else {
result = '0' + value;
}
Converting Int to Char
I take it that OP wants more that just a 1 digit conversion as radix was supplied.
To convert an int into a string, (not just 1 char) there is the sprintf(buf, "%d", value) approach.
To do so to any radix, string management becomes an issue as well as dealing the corner case of INT_MIN
The following C99 solution returns a char* whose lifetime is valid to the end of the block. It does so by providing a compound literal via the macro.
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
// Maximum buffer size needed
#define ITOA_BASE_N (sizeof(unsigned)*CHAR_BIT + 2)
char *itoa_base(char *s, int x, int base) {
s += ITOA_BASE_N - 1;
*s = '\0';
if (base >= 2 && base <= 36) {
int x0 = x;
do {
*(--s) = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[abs(x % base)];
x /= base;
} while (x);
if (x0 < 0) {
*(--s) = '-';
}
}
return s;
}
#define TO_BASE(x,b) itoa_base((char [ITOA_BASE_N]){0} , (x), (b))
Sample usage and tests
void test(int x) {
printf("base10:% 11d base2:%35s base36:%7s ", x, TO_BASE(x, 2), TO_BASE(x, 36));
printf("%ld\n", strtol(TO_BASE(x, 36), NULL, 36));
}
int main(void) {
test(0);
test(-1);
test(42);
test(INT_MAX);
test(-INT_MAX);
test(INT_MIN);
}
Output
base10: 0 base2: 0 base36: 0 0
base10: -1 base2: -1 base36: -1 -1
base10: 42 base2: 101010 base36: 16 42
base10: 2147483647 base2: 1111111111111111111111111111111 base36: ZIK0ZJ 2147483647
base10:-2147483647 base2: -1111111111111111111111111111111 base36:-ZIK0ZJ -2147483647
base10:-2147483648 base2: -10000000000000000000000000000000 base36:-ZIK0ZK -2147483648
Ref How to use compound literals to fprintf() multiple formatted numbers with arbitrary bases?
Check out the ascii table
The values stored in a char are interpreted as the characters corresponding to that table. The value of 10 is a newline
So characters in C are based on ASCII (or UTF-8 which is backwards-compatible with ascii codes). This means that under the hood, "A" is actually the number "65" (except in binary rather than decimal). All a "char" is in C is an integer with enough bytes to represent every ASCII character. If you want to convert an int to a char, you'll need to instruct the computer to interpret the bytes of an int as ASCII values - and it's been a while since I've done C, but I believe the compiler will complain since char holds fewer bytes than int. This means we need a function, as you've written. Thus,
if(value < 10) return '0'+value;
return 'A'+value-10;
will be what you want to return from your function. Keep your bounds checks with "radix" as you've done, imho that is good practice in C.
1. Converting int to char by type casting
Source File charConvertByCasting.c
#include <stdio.h>
int main(){
int i = 66; // ~~Type Casting Syntax~~
printf("%c", (char) i); // (type_name) expression
return 0;
}
Executable charConvertByCasting.exe command line output:
C:\Users\boqsc\Desktop\tcc>tcc -run charconvert.c
B
Additional resources:
https://www.tutorialspoint.com/cprogramming/c_type_casting.htm
https://www.tutorialspoint.com/cprogramming/c_data_types.htm
2. Convert int to char by assignment
Source File charConvertByAssignment.c
#include <stdio.h>
int main(){
int i = 66;
char c = i;
printf("%c", c);
return 0;
}
Executable charConvertByAssignment.exe command line output:
C:\Users\boqsc\Desktop\tcc>tcc -run charconvert.c
B
You can do
char a;
a = '0' + 5;
You will get character representation of that number.
Borrowing the idea from the existing answers, i.e. making use of array index.
Here is a "just works" simple demo for "integer to char[]" conversion in base 10, without any of <stdio.h>'s printf family interfaces.
Test:
$ cc -o testint2str testint2str.c && ./testint2str
Result: 234789
Code:
#include <stdio.h>
#include <string.h>
static char digits[] = "0123456789";
void int2str (char *buf, size_t sz, int num);
/*
Test:
cc -o testint2str testint2str.c && ./testint2str
*/
int
main ()
{
int num = 234789;
char buf[1024] = { 0 };
int2str (buf, sizeof buf, num);
printf ("Result: %s\n", buf);
}
void
int2str (char *buf, size_t sz, int num)
{
/*
Convert integer type to char*, in base-10 form.
*/
char *bufp = buf;
int i = 0;
// NOTE-1
void __reverse (char *__buf, int __start, int __end)
{
char __bufclone[__end - __start];
int i = 0;
int __nchars = sizeof __bufclone;
for (i = 0; i < __nchars; i++)
{
__bufclone[i] = __buf[__end - 1 - i];
}
memmove (__buf, __bufclone, __nchars);
}
while (num > 0)
{
bufp[i++] = digits[num % 10]; // NOTE-2
num /= 10;
}
__reverse (buf, 0, i);
// NOTE-3
bufp[i] = '\0';
}
// NOTE-1:
// "Nested function" is GNU's C Extension. Put it outside if not
// compiled by GCC.
// NOTE-2:
// 10 can be replaced by any radix, like 16 for hexidecimal outputs.
//
// NOTE-3:
// Make sure inserting trailing "null-terminator" after all things
// done.
NOTE-1:
"Nested function" is GNU's C Extension. Put it outside if not
compiled by GCC.
NOTE-2:
10 can be replaced by any radix, like 16 for hexidecimal outputs.
NOTE-3:
Make sure inserting trailing "null-terminator" after all things
done.
Hi I am pretty new to coding and I really need help.
Basically I have a decimal value and I converted it to a binary value.
Using this method
long decimalToBinary(long n)
{
int remainder;
long binary = 0, i = 1;
while(n != 0)
{
remainder = n%2;
n = n/2;
binary= binary + (remainder*i);
i = i*10;
}
return binary;
}
And I want to give each character of the binary into it's own space inside an array. However, I can't seem to save digits from the return values in my string array. I think it has something to do with converting the long to string but I could be wrong! Here is what I have so far.
I do not want to use sprintf(); I do not wish to print the value I just want the value stored inside it so that the if conditions can read it. Any help would be appreciated!
int decimalG = 24;
long binaryG = decimalToBinary(decimalG);
char myStringG[8] = {binaryG};
for( int i = 0; i<8; i++)
{
if (myStringG[i] == '1' )
{
T1();
}
else
{
T0();
}
}
In this case since the decimal is 24, the binary would be 11000 therefore it should execute the the function T1(); 2 times and T0() 6 times. But it doesn't do that and I can't seem to find the answer to store the saved values in the array.
*Ps the Itoa(); function is also not an option. Thanks in Advance! :)
As the post is tagged arm using malloc() might not be the best approach, although the simplest. If you insist on using arrays:
#include <stdio.h>
#include <stdlib.h>
int decimalToBinary(long n, char out[], int len)
{
long remainder;
// C arrays are zero based
len--;
// TODO: check if the input is reasonable
while (n != 0) {
// pick a bit
remainder = n % 2;
// shift n one bit to the right
// It is the same as n = n/2 but
// is more telling of what you are doing:
// shifting the whole thing to the right
// and drop the least significant bit
n >>= 1;
// Check boundaries! Always!
if (len < 0) {
// return zero for "Fail"
return 0;
}
// doing the following four things at once:
// cast remainder to char
// add the numerical value of the digit "0"
// put it into the array at place len
// decrement len
out[len--] = (char) remainder + '0';
}
// return non-zero value for "All OK"
return 1;
}
// I don't know what you do here, but it
// doesn't matter at all for this example
void T0()
{
fputc('0', stdout);
}
void T1()
{
fputc('1', stdout);
}
int main()
{
// your input
int decimalG = 24;
// an array able to hold 8 (eight) elements of type char
char myStringG[8];
// call decimalToBinary with the number, the array and
// the length of that array
if (!decimalToBinary(decimalG, myStringG, 8)) {
fprintf(stderr, "decimalToBinary failed\n");
exit(EXIT_FAILURE);
}
// Print the whole array
// How to get rid of the leading zeros is left to you
for (int i = 0; i < 8; i++) {
if (myStringG[i] == '1') {
T1();
} else {
T0();
}
}
// just for the optics
fputc('\n', stdout);
exit(EXIT_SUCCESS);
}
Computing the length needed is tricky, but if you know the size of long your Micro uses (8, 16, 32, or even 64 bit these days) you can take that as the maximum size for the array. Leaves the leading zeros but that should not be a problem, or is it?
To achieve your goal, you don't have to convert a decimal value to binary:
unsigned decimalG = 24; // Assumed positive, for negative values
// have implementation-defined representation
for (; decimalG; decimalG >>= 1) {
if(decimalG & 1) {
// Do something
} else {
// Do something else
}
}
Or you can use a union, but I'm not sure whether this approach is well defined by the standard.
If you stick to writing decimalToBinary, note that you'll have to use an array:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
char *decimalToBinary(unsigned n);
int
main(void) {
int decimalG = 15;
char *binary = decimalToBinary(decimalG);
puts(binary);
free(binary);
}
char *
decimalToBinary(unsigned n) {
// Don't forget to free() it after use!!!
char *binary = malloc(sizeof(unsigned) * CHAR_BIT + 1);
if(!binary) return 0;
size_t i;
for (i = 0; i < sizeof(unsigned) * CHAR_BIT; i++) {
binary[i] = '0' + ((n >> i) & 1); // in reverse order
}
binary[i] = 0;
return binary;
}
Use the itoa (integer-to-ascii) function.
http://www.cplusplus.com/reference/cstdlib/itoa/
EDIT: Correction:
Don't be an idiot, use the itoa (integer-to-ascii) function.
http://www.cplusplus.com/reference/cstdlib/itoa/
EDIT:
Maybe I wasn't clear enough. I saw the line that said:
*Ps the Itoa(); function is also not an option.
This is completely unreasonable. You want to reinvent the wheel, but you want someone else to do it? What do you possibly have against itoa? It's part of the standard. It will always exist, no matter what platform you're targeting or version of C that you're using.
I want to give each character of the binary into it's own
space inside an array. However, I can't seem to save digits
from the return values in my string array.
There are a number of ways to approach this, if I understand what you are asking. First, there is no need to actually store the results of the binary representation of your number in an array to call T1() or T0() based on the bit value of any given bit that makes up the number.
Take your example 24 (binary 11000). If I read your post correctly you state:
In this case since the decimal is 24, the binary
would be 11000 therefore it should execute the the
function T1() 2 times and T0() 6 times.
(I'm not sure where you get 6 times, it looks like you intended that T0() would be called 3 times)
If you have T0 and T1 defined, for example, to simply let you know when they are called, e.g.:
void T1 (void) { puts ("T1 called"); }
void T0 (void) { puts ("T0 called"); }
You can write a function (say named callt) to call T1 for each 1-bit and T0 for each 0-bit in a number simply as follows:
void callt (const unsigned long v)
{
if (!v) { putchar ('0'); return; };
size_t sz = sizeof v * CHAR_BIT;
unsigned long rem = 0;
while (sz--)
if ((rem = v >> sz)) {
if (rem & 1)
T1();
else
T0();
}
}
So far example if you passed 24 to the function callt (24), the output would be:
$ ./bin/dec2bincallt
T1 called
T1 called
T0 called
T0 called
T0 called
(full example provided at the end of answer)
On the other hand, if you really do want to give each character of the binary into it's own space inside an array, then you would simply need to pass an array to capture the bit values (either the ASCII character representations for '0' and '1', or just 0 and 1) instead of calling T0 and T1 (you would also add a few lines to handle v=0 and also the nul-terminating character if you will use the array as a string) For example:
/** copy 'sz' bits of the binary representation of 'v' to 's'.
* returns pointer to 's', on success, empty string otherwise.
* 's' must be adequately sized to hold 'sz + 1' bytes.
*/
char *bincpy (char *s, unsigned long v, unsigned sz)
{
if (!s || !sz) {
*s = 0;
return s;
}
if (!v) {
*s = '0';
*(s + 1) = 0;
return s;
}
unsigned i;
for (i = 0; i < sz; i++)
s[i] = (v >> (sz - 1 - i)) & 1 ? '1' : '0';
s[sz] = 0;
return s;
}
Let me know if you have any additional questions. Below are two example programs. Both take as their first argument the number to convert (or to process) as binary (default: 24 if no argument is given). The first simply calls T1 for each 1-bit and T0 for each 0-bit:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h> /* for CHAR_BIT */
void callt (const unsigned long v);
void T1 (void) { puts ("T1 called"); }
void T0 (void) { puts ("T0 called"); }
int main (int argc, char **argv) {
unsigned long v = argc > 1 ? strtoul (argv[1], NULL, 10) : 24;
callt (v);
return 0;
}
void callt (const unsigned long v)
{
if (!v) { putchar ('0'); return; };
size_t sz = sizeof v * CHAR_BIT;
unsigned long rem = 0;
while (sz--)
if ((rem = v >> sz)) {
if (rem & 1) T1(); else T0();
}
}
Example Use/Output
$ ./bin/dec2bincallt
T1 called
T1 called
T0 called
T0 called
T0 called
$ ./bin/dec2bincallt 11
T1 called
T0 called
T1 called
T1 called
The second stores each bit of the binary representation for the value as a nul-terminated string and prints the result:
#include <stdio.h>
#include <stdlib.h>
#define BITS_PER_LONG 64 /* define as needed */
char *bincpy (char *s, unsigned long v, unsigned sz);
int main (int argc, char **argv) {
unsigned long v = argc > 1 ? strtoul (argv[1], NULL, 10) : 24;
char array[BITS_PER_LONG + 1] = "";
printf (" values in array: %s\n", bincpy (array, v, 16));
return 0;
}
/** copy 'sz' bits of the binary representation of 'v' to 's'.
* returns pointer to 's', on success, empty string otherwise.
* 's' must be adequately sized to hold 'sz + 1' bytes.
*/
char *bincpy (char *s, unsigned long v, unsigned sz)
{
if (!s || !sz) {
*s = 0;
return s;
}
if (!v) {
*s = '0';
*(s + 1) = 0;
return s;
}
unsigned i;
for (i = 0; i < sz; i++)
s[i] = (v >> (sz - 1 - i)) & 1 ? '1' : '0';
s[sz] = 0;
return s;
}
Example Use/Output
(padding to 16 bits)
$ ./bin/dec2binarray
values in array: 0000000000011000
$ ./bin/dec2binarray 11
values in array: 0000000000001011
I have a hexadecimal value "F69CF355B6231FDBD91EB1E22B61EA1F" in a string and I am using this value in my program by hardcoding the value in an unsigned char variable like this:
unsigned char a[] = { 0xF6 ,0x9C ,0xF3 ,0x55 ,0xB6 ,0x23 ,0x1F ,0xDB ,0xD9 ,0x1E ,0xB1 ,0xE2 ,0x2B ,0x61 ,0xEA ,0x1F};
Is there any function or any other method by which I can take the value from a string and put it into an unsigned variable in the hexadecimal format by packing it?
#include <stdio.h>
#include <ctype.h>
int hctoi(const char h){
if(isdigit(h))
return h - '0';
else
return toupper(h) - 'A' + 10;
}
int main(void){
const char cdata[]="F69CF355B6231FDBD91EB1E22B61EA1F";
unsigned char udata[(sizeof(cdata)-1)/2];
const char *p;
unsigned char *up;
for(p=cdata,up=udata;*p;p+=2,++up){
*up = hctoi(p[0])*16 + hctoi(p[1]);
}
{ //check code
int i;
for(i=0;i<sizeof(udata);++i)
printf("%02X", udata[i]);
}
return 0;
}
You can translate a hexadecimal value in a string into a value using sscanf. If you want an array of values, then you could write a function to split up the input string into two character segments and use sscanf to convert each piece. (I haven't done C for an eternity so I don't know if that's a good way to do it.)
If it's for 32 single hex-values (16 bytes, 128bit) only then you might take look at the methods provided by libuuid.
libuuid is part of the e2fsprogs package. Anyhow some linux distros, Debian for example, ship libuuid as a separate package. To use the Debian package for your developement you also need to look here.
Check this answer for doing this stuff in c++ using sscanf().
For c, it would be something like this:
char *str = "F69CF355B6231FDBD91EB1E22B61EA1F";
char substr[3] = "__";
unsigned char *a = NULL;
len = strlen(str);
a = malloc(sizeof(unsigned char)*(len/2)+1);
for ( i = 0; i < len/2; i++) {
substr[0] = str[i*2];
substr[1] = str[i*2 + 1];
sscanf( substr, "%hx", &a[i] );
}
free(a);
Introduce auxiliary functions data_length and data_get to easily iterate over your data. The following program dumps unpacked unsigned chars on stdout, one per line:
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
/* htoi(H)
Return the value associated to the hexadecimal digit H. */
int
htoi(char h)
{
int a = -1;
if(isdigit(h))
{
a = h - '0';
}
else
{
a = toupper(h) - 'A' + 10;
}
return a;
}
/* data_length(D)
The length of the data stored at D. */
int
data_length(const char* d)
{
return strlen(d) / 2;
}
/* data_get(D, K)
Return the K-th unsigned char located encoded in d. */
unsigned char
data_get(const char *d, int k)
{
return htoi(d[2*k]) * 0x10 +
htoi((d+1)[2*k]);
}
int
main()
{
const char cdata[]="F69CF355B6231FDBD91EB1E22B61EA1F";
for(int i = 0; i < data_length(cdata); ++i)
{
printf("0x%02hhx\n", data_get(cdata, i));
}
return EXIT_SUCCESS;
}
I was wondering if my implementation of an "itoa" function is correct. Maybe you can help me getting it a bit more "correct", I'm pretty sure I'm missing something. (Maybe there is already a library doing the conversion the way I want it to do, but... couldn't find any)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char * itoa(int i) {
char * res = malloc(8*sizeof(int));
sprintf(res, "%d", i);
return res;
}
int main(int argc, char *argv[]) {
...
// Yet, another good itoa implementation
// returns: the length of the number string
int itoa(int value, char *sp, int radix)
{
char tmp[16];// be careful with the length of the buffer
char *tp = tmp;
int i;
unsigned v;
int sign = (radix == 10 && value < 0);
if (sign)
v = -value;
else
v = (unsigned)value;
while (v || tp == tmp)
{
i = v % radix;
v /= radix;
if (i < 10)
*tp++ = i+'0';
else
*tp++ = i + 'a' - 10;
}
int len = tp - tmp;
if (sign)
{
*sp++ = '-';
len++;
}
while (tp > tmp)
*sp++ = *--tp;
return len;
}
// Usage Example:
char int_str[15]; // be careful with the length of the buffer
int n = 56789;
int len = itoa(n,int_str,10);
The only actual error is that you don't check the return value of malloc for null.
The name itoa is kind of already taken for a function that's non-standard, but not that uncommon. It doesn't allocate memory, rather it writes to a buffer provided by the caller:
char *itoa(int value, char * str, int base);
If you don't want to rely on your platform having that, I would still advise following the pattern. String-handling functions which return newly allocated memory in C are generally more trouble than they're worth in the long run, because most of the time you end up doing further manipulation, and so you have to free lots of intermediate results. For example, compare:
void delete_temp_files() {
char filename[20];
strcpy(filename, "tmp_");
char *endptr = filename + strlen(filename);
for (int i = 0; i < 10; ++i) {
itoa(endptr, i, 10); // itoa doesn't allocate memory
unlink(filename);
}
}
vs.
void delete_temp_files() {
char filename[20];
strcpy(filename, "tmp_");
char *endptr = filename + strlen(filename);
for (int i = 0; i < 10; ++i) {
char *number = itoa(i, 10); // itoa allocates memory
strcpy(endptr, number);
free(number);
unlink(filename);
}
}
If you had reason to be especially concerned about performance (for instance if you're implementing a stdlib-style library including itoa), or if you were implementing bases that sprintf doesn't support, then you might consider not calling sprintf. But if you want a base 10 string, then your first instinct was right. There's absolutely nothing "incorrect" about the %d format specifier.
Here's a possible implementation of itoa, for base 10 only:
char *itobase10(char *buf, int value) {
sprintf(buf, "%d", value);
return buf;
}
Here's one which incorporates the snprintf-style approach to buffer lengths:
int itobase10n(char *buf, size_t sz, int value) {
return snprintf(buf, sz, "%d", value);
}
A good int to string or itoa() has these properties;
Works for all [INT_MIN...INT_MAX], base [2...36] without buffer overflow.
Does not assume int size.
Does not require 2's complement.
Does not require unsigned to have a greater positive range than int. In other words, does not use unsigned.
Allows use of '-' for negative numbers, even when base != 10.
Tailor the error handling as needed. (needs C99 or later):
char* itostr(char *dest, size_t size, int a, int base) {
// Max text needs occur with itostr(dest, size, INT_MIN, 2)
char buffer[sizeof a * CHAR_BIT + 1 + 1];
static const char digits[36] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (base < 2 || base > 36) {
fprintf(stderr, "Invalid base");
return NULL;
}
// Start filling from the end
char* p = &buffer[sizeof buffer - 1];
*p = '\0';
// Work with negative `int`
int an = a < 0 ? a : -a;
do {
*(--p) = digits[-(an % base)];
an /= base;
} while (an);
if (a < 0) {
*(--p) = '-';
}
size_t size_used = &buffer[sizeof(buffer)] - p;
if (size_used > size) {
fprintf(stderr, "Scant buffer %zu > %zu", size_used , size);
return NULL;
}
return memcpy(dest, p, size_used);
}
I think you are allocating perhaps too much memory. malloc(8*sizeof(int)) will give you 32 bytes on most machines, which is probably excessive for a text representation of an int.
i found an interesting resource dealing with several different issues with the itoa implementation
you might wanna look it up too
itoa() implementations with performance tests
I'm not quite sure where you get 8*sizeof(int) as the maximum possible number of characters -- ceil(8 / (log(10) / log(2))) yields a multiplier of 3*. Additionally, under C99 and some older POSIX platforms you can create an accurately-allocating version with sprintf():
char *
itoa(int i)
{
int n = snprintf(NULL, 0, "%d", i) + 1;
char *s = malloc(n);
if (s != NULL)
snprintf(s, n, "%d", i);
return s;
}
HTH
You should use a function in the printf family for this purpose. If you'll be writing the result to stdout or a file, use printf/fprintf. Otherwise, use snprintf with a buffer big enough to hold 3*sizeof(type)+2 bytes or more.
sprintf is quite slow, if performance matters it is probably not the best solution.
if the base argument is a power of 2 the conversion can be done with a shift and masking, and one can avoid reversing the string by recording the digits from the highest positions. For instance, something like this for base=16
int num_iter = sizeof(int) / 4;
const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/* skip zeros in the highest positions */
int i = num_iter;
for (; i >= 0; i--)
{
int digit = (value >> (bits_per_digit*i)) & 15;
if ( digit > 0 ) break;
}
for (; i >= 0; i--)
{
int digit = (value >> (bits_per_digit*i)) & 15;
result[len++] = digits[digit];
}
For decimals there is a nice idea to use a static array big enough to record the numbers in the reversed order, see here
Integer-to-ASCII needs to convert data from a standard integer type
into an ASCII string.
All operations need to be performed using pointer arithmetic, not array indexing.
The number you wish to convert is passed in as a signed 32-bit integer.
You should be able to support bases 2 to 16 by specifying the integer value of the base you wish to convert to (base).
Copy the converted character string to the uint8_t* pointer passed in as a parameter (ptr).
The signed 32-bit number will have a maximum string size (Hint: Think base 2).
You must place a null terminator at the end of the converted c-string Function should return the length of the converted data (including a negative sign).
Example my_itoa(ptr, 1234, 10) should return an ASCII string length of 5 (including the null terminator).
This function needs to handle signed data.
You may not use any string functions or libraries.
.
uint8_t my_itoa(int32_t data, uint8_t *ptr, uint32_t base){
uint8_t cnt=0,sgnd=0;
uint8_t *tmp=calloc(32,sizeof(*tmp));
if(!tmp){exit(1);}
else{
for(int i=0;i<32;i++){
if(data<0){data=-data;sgnd=1;}
if(data!=0){
if(data%base<10){
*(tmp+i)=(data%base)+48;
data/=base;
}
else{
*(tmp+i)=(data%base)+55;
data/=base;
}
cnt++;
}
}
if(sgnd){*(tmp+cnt)=45;++cnt;}
}
my_reverse(tmp, cnt);
my_memcopy(tmp,ptr,cnt);
return ++cnt;
}
ASCII-to-Integer needs to convert data back from an ASCII represented string into an integer type.
All operations need to be performed using pointer arithmetic, not array indexing
The character string to convert is passed in as a uint8_t * pointer (ptr).
The number of digits in your character set is passed in as a uint8_t integer (digits).
You should be able to support bases 2 to 16.
The converted 32-bit signed integer should be returned.
This function needs to handle signed data.
You may not use any string functions or libraries.
.
int32_t my_atoi(uint8_t *ptr, uint8_t digits, uint32_t base){
int32_t sgnd=0, rslt=0;
for(int i=0; i<digits; i++){
if(*(ptr)=='-'){*ptr='0';sgnd=1;}
else if(*(ptr+i)>'9'){rslt+=(*(ptr+i)-'7');}
else{rslt+=(*(ptr+i)-'0');}
if(!*(ptr+i+1)){break;}
rslt*=base;
}
if(sgnd){rslt=-rslt;}
return rslt;
}
I don't know about good, but this is my implementation that I did while learning C
static int ft_getintlen(int value)
{
int l;
int neg;
l = 1;
neg = 1;
if (value < 0)
{
value *= -1;
neg = -1;
}
while (value > 9)
{
l++;
value /= 10;
}
if (neg == -1)
{
return (l + 1);
}
return (l);
}
static int ft_isneg(int n)
{
if (n < 0)
return (-1);
return (1);
}
static char *ft_strcpy(char *dest, const char *src)
{
unsigned int i;
i = 0;
while (src[i] != '\0')
{
dest[i] = src[i];
i++;
}
dest[i] = src[i];
return (dest);
}
char *ft_itoa(int n)
{
size_t len;
char *instr;
int neg;
neg = ft_isneg(n);
len = ft_getintlen(n);
instr = (char *)malloc((sizeof(char) * len) + 1);
if (n == -2147483648)
return (ft_strcpy(instr, "-2147483648"));
if (!instr)
return (NULL);
if (neg == -1)
n *= -1;
instr[len--] = 0;
if (n == 0)
instr[len--] = 48;
while (n)
{
instr[len--] = ((n % 10) + 48);
n /= 10;
}
if (neg == -1)
instr[len] = '-';
return (instr);
}
This should work:
#include <string.h>
#include <stdlib.h>
#include <math.h>
char * itoa_alloc(int x) {
int s = x<=0 ? 1 ? 0; // either space for a - or for a 0
size_t len = (size_t) ceil( log10( abs(x) ) );
char * str = malloc(len+s + 1);
sprintf(str, "%i", x);
return str;
}
If you don't want to have to use the math/floating point functions (and have to link in the math libraries) you should be able to find non-floating point versions of log10 by searching the Web and do:
size_t len = my_log10( abs(x) ) + 1;
That might give you 1 more byte than you needed, but you'd have enough.
There a couple of suggestions I might make. You can use a static buffer and strdup to avoid repeatedly allocating too much memory on subsequent calls. I would also add some error checking.
char *itoa(int i)
{
static char buffer[12];
if (snprintf(buffer, sizeof(buffer), "%d", i) < 0)
return NULL;
return strdup(buffer);
}
If this will be called in a multithreaded environment, remove "static" from the buffer declaration.
This is chux's code without safety checks and the ifs. Try it online:
char* itostr(char * const dest, size_t const sz, int a, int const base) {
bool posa = a >= 0;
char buffer[sizeof a * CHAR_BIT + 1];
static const char digits[36] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char* p = &buffer[sizeof buffer - 1];
do {
*(p--) = digits[abs(a % base)];
a /= base;
} while (a);
*p = '-';
p += posa;
size_t s = &buffer[sizeof(buffer)] - p;
memcpy(dest, p, s);
dest[s] = '\0';
return dest;
}
main()
{
int i=1234;
char stmp[10];
#if _MSC_VER
puts(_itoa(i,stmp,10));
#else
puts((sprintf(stmp,"%d",i),stmp));
#endif
return 0;
}