Someone asked me to write this code as an exercise in C. When they looked over what I had written, they immediately told me that there was a huge error regarding memory management. Being a simple exercise, they wanted me to find and fix this error. There must be a gap in my knowledge, or I must be overlooking something very obvious, because I cannot for the life of me find it. If someone could help me figure it out, I would be very grateful.
Here is the code:
char int_to_char(int number){
if (number > 9) return (char)(((int)'A') + number - 10);
else return (char)(((int)'0') + number);
}
int change_base(char* output, int buffer_size, int decimal_number, int base){
//check for valid parameters
if((base < 2) || (base > 26)) return -1; //range error
//ready variables
int output_i = 0;
int tmp_string_i = 0;
int dividend;
char remainder;
char * tmp_string = calloc(buffer_size, sizeof(char));
memset(output, '\0', buffer_size*sizeof(char));
//check for negative input
if(decimal_number < 0){
output[0] = '-';
dividend = -decimal_number;
output_i++;
}
else dividend = decimal_number;
//find digits
while(dividend / base != 0){
remainder = int_to_char(dividend % base);
dividend = dividend / base;
tmp_string[tmp_string_i] = remainder;
tmp_string_i++;
if(tmp_string_i + 1 > buffer_size){ //+1 for the extra negative sign
free(tmp_string);
return -2; //buffer size error
}
}
//add last digit to string
remainder = int_to_char(dividend);
tmp_string[tmp_string_i] = remainder;
//copy tmp_string to output in reverse order
for(; tmp_string_i >= 0; tmp_string_i--){
output[output_i] = tmp_string[tmp_string_i];
output_i++;
}
free(tmp_string);
return 0;
}
Also worthy of note, I have run this code through Valgrind to find any common memory mistakes, but it reports no errors. I don't know very much about the advanced features or nuances of Valgrind.
Lastly, I would be very happy for any comments on how I could improve the overall effectiveness and readability of this code.
If to say about one "huge error" then this error is the awful code itself.:)l
So if there are other errors then in fact they do not deserve to be discussed until the code will be rewritten.
For example do you know that if some integral number is negative then after statement like this
number = - number;
the number can be as before negative?:)
I think that "somebody" meant by "huge error" that your string is not zero-treminated.:) Consider a situation wnen the number has only one digit and buffer_size is equal to 1
And why does buffer_size have type int instead of size_t?
Also in my opinion it is a bad idea to allocate additional buffer for such a conversion.
Related
I'm passing almost all leetCode tests with this, but not understanding why the output is wrong ("/0") when the input is:
a = "10100000100100110110010000010101111011011001101110111111111101000000101111001110001111100001101"
b = "110101001011101110001111100110001010100001101011101010000011011011001011101111001100000011011110011"
Anyone has an idea to what is not working ?
Thanks
#include <stdio.h>
#include <stdlib.h>
char * sumBinary(long int binary1, long int binary2, char * result);
char * addBinary(char * a, char * b)
{
char * result;
long int a_int;
long int b_int;
a_int = atoi(a);
b_int = atoi(b);
result = malloc(sizeof(*result) * 1000);
if (!result)
return (NULL);
sumBinary(a_int, b_int, result);
return (result);
}
char * sumBinary(long int binary1, long int binary2, char * result)
{
int i;
int t;
int rem;
int sum[1000];
i = 0;
t = 0;
rem = 0;
if ((binary1 == 0) && (binary2 == 0))
{
result[0] = '0';
result[1] = '\0';
}
else
{
while (binary1 != 0 || binary2 != 0)
{
sum[i++] = (binary1 %10 + binary2 % 10 + rem) % 2;
rem = (binary1 %10 + binary2 % 10 + rem) / 2;
binary1 = binary1 / 10;
binary2 = binary2 / 10;
}
if (rem != 0)
sum[i++] = rem;
--i;
while (i >= 0)
{
result[t] = sum[i] + '0';
t++;
i--;
}
result[t] = '\0';
}
return (result);
}
For a start, you should be using atol(3), not atoi(3) if you're using long int. But that's not the main issue here.
atol(3) and atoi(3) expect strings containing decimal numbers, not binary, so that's not going to work well for you. You would need strtol(3), which you can tell to expect a string in ASCII binary. But again, this is not the main issue.
You don't give the question text, but I'm guessing they want you to add two arbitrarily-long ASCII-binary strings, resulting in an ASCII-binary string.
I imagine their expectation, given it's arbitrarily-long, is that you would be working entirely in the string domain. So you'd allocate for a string whose length is two greater than the longer of the two you get as parameters (+1 for the terminal NUL, the other +1 for a potential overflow digit).
Then you start from the end, working back to the start, adding the corresponding digits of the parameter strings, placing the results into the result string starting from its end (allowing for that terminal NUL), adding as if you were doing it by hand.
Don't forget to add a leading zero to the result string, if you don't overflow into that position.
Note that I'm not going to write the code for you. This is either a learning exercise or a test: either way, you need to do the coding so you can learn from it.
I have the following program that converts decimal to binary:
#include <stdio.h>
#include <string.h>
int main() {
printf("Number (decimal): ");
int no;
scanf("%d", &no);
char bin[64];
while (no > 0) {
for (int i = strlen(bin); i > 0; i--) {
bin[i] = bin[i - 1];
}
int bit = no % 2;
char digit = bit + '0';
bin[0] = digit;
no /= 2;
}
printf("%s", bin);
return 0;
}
The program works correctly, but randomly the string "ttime__vdso_get" gets appended on the end.
The numbers that make it happen are different every time I compile.
1: 1
2: 01ttime_vsdo_get
3: 10ttime_vsdo_get
It becomes a little different when the numbers get bigger:
100039: 11000011011000111ttime__vdso_getm#
10000000000000000000000000000: ttime
What is happening?
If I had to diagnose it I'd say that I've managed to make a compiling program that's pulling memory from the wrong places. I don't know how how I managed to do it, though.
I'm using GCC, if it matters.
Just do char bin[64] = "";, never forget that a valid string is nul terminatedM#��M#.
And strlen() return an size_t !
I can also advice you to use char bin[sizeof no * CHAR_BIT + 1] = ""; that will use a correct maximum size for your string.
It may be because of this line of code :
for (int i = strlen(bin); i > 0; i--) {
bin[i] = bin[i - 1];
}
Try replacing strlen(bin) with 63.
It may also be a good idea to initialize your array bin with 0s.
try filling varible
char bin[64]
with 0
char decimalToHexadecimal(long int decimalNumber)
{
long int quotient;
long int remainder;
static char hexDecNum[100];
int i=0;
quotient = decimalNumber;
while (quotient != 0)
{
remainder = quotient % 16;
// to convert integer into character
if (remainder < 10)
{
remainder = remainder + 48;
}
else
{
remainder = remainder + 55;
}
hexDecNum[i++] = remainder;
quotient = quotient / 16;
}
}
This user defined function will convert decimal number to hexadecimal number.
I wanted to make function that will not use any library function like printf,scanf etc. I and to get return hexadecimal value of a decimal number from this function.
But, I am confused how to get return hexadecimal number from this function?
It's better to use an explicit string of the desired character set, that way you can remove any assumptions about the encoding, which is very good for portability.
Although C requires that the digits 0 through 9 are encoded using adjacent code points (i.e. '1' - '0' must equal 1, and so on), no such guarantee is made for the letters.
Also, returning a string requires heap allocation, a static buffer (which makes the function harder to use), or simply accepting the string from the caller which is often the best choice.
It's important to realize that the typical technique of extracting the remainder from division by 16 (or just masking out the four right-most bits) generates bits "from the right", whereas typical string-building runs from the left. This has to be taken into account as well, or you'd generate "d00f" when given 0xf00d.
Here's how it could look:
char * num2hex(char *buf, size_t buf_max, unsigned long number)
{
if(buf_max < 2)
return NULL;
char * put = buf + buf_max; // Work backwards.
*--put = '\0'; // Terminate the resulting string.
do
{
if(put == buf)
return NULL;
const unsigned int digit = number & 15;
*--put = "0123456789abcdef"[digit];
number >>= 4;
} while(number != 0);
return put;
}
This function returns the start of the built string (which is "right-aligned" in the provided buffer, so it's not at the start of it), or NULL if it runs out of space.
Note: yeah, this is perhaps a bit too terse, of course the digit set could be extracted out and given a name, but it's also pretty obvious what its purpose is, and indexing a literal is handy (some people don't seem to realize it's doable) and somewhat instructive to show off.
Read the chapter about strings in your C textbook.
One solution is to return a pointer to char:
char *decimalToHexadecimal(long int decimalNumber)
{
long int quotient;
long int remainder;
static char hexDecNum[100]; // must be static
quotient = decimalNumber;
int i = 0; // // <<<< you forgot to declare i
while (quotient != 0)
{
remainder = quotient % 16;
// to convert integer into character
if (remainder < 10)
{
remainder = remainder + 48;
}
else
{
remainder = remainder + 55;
}
hexDecNum[i++] = remainder;
quotient = quotient / 16;
}
hexDecNum[i] = 0; // <<< you forgot the NUL string terminator
return hexDecNum; // <<< you forgot to return something
}
int main()
{
printf("%s\n", decimalToHexadecimal(0x1234));
}
The hexDecNum buffer must be static, because you are returning a pointer to this buffer, and this buffer will cease to exist once you have returned from the decimalToHexadecimal function, because it's a local variable. Modern compilers will usually emit a warning if you return the address of a local variable.
The function is still not quite what you want. I leave it as an exercise to correct it.
Edit
Another approach for conversion is this: the decimal number is actually represented as a binary number (as all numbers BTW), so we don't even need division; we just can decompose the number into 4 bit nibbles (0000 to 1111) and transform these nibbles into a hexadecimal digits (0..9, A..F):
char *decimalToHexadecimal(long int decimalNumber)
{
static char hexDecNum[100];
int i;
for (i = 0; i < sizeof(decimalNumber) * 2; i++)
{
int digit = decimalNumber & 0xf;
if (digit >= 10)
digit += 'A' - 10; // better than writing 55
else
digit += '0'; // better than writing 48
hexDecNum[i] = digit;
decimalNumber >>= 4;
}
hexDecNum[i] = 0;
return hexDecNum;
}
The function suffers from the same problem as your original function. Improving it is left as an exercise.
I have a string with length 28, that represent 1 number in radix 16.
long num=strtol(str,NULL,16);
But it is worked for small strings but not long string (it gave me a negative result). So for long string how can I translate it?
//--------------------------
Well I think there is only one way without creating bicycle. Use GMP. So simple code with how to use GMP will be good answer.
Well Thanks to #DavidSchwartz who helped me in comments. So the solution is to use GMP library:
mpz_t res;
mpz_init_set_str (res, str, 16);
gmp_fprintf(f1,"%s",mpz_get_str(NULL,10,res));
mpz_clear (res);
May be something wrong, but it worked somehow. If you find a mistake let me know, Ill change it for future people.
Since code is starting with a string representing an integer in base 16, could use a string to represent the base 10 number too.
Simple enough to convert one digit at a time.
Space management left for OP.
#include <stdio.h>
#include <stdlib.h>
char *baseNtobase10(char *s10, const char *s16, int n) {
strcpy(s10, "0");
size_t len = strlen(s10);
while (*s16) {
char sdigit[] = { *s16, 0 };
char *endptr;
int digit = (int) strtol(sdigit, &endptr, n);
if (endptr == sdigit) return NULL; // detect illegal digits
// multiple s10[] = s10[] * n + digit
for (size_t i = len; i-- > 0; ) {
digit += (s10[i] - '0') * n;
s10[i] = digit % 10 + '0';
digit /= 10;
}
// handle carry oout
while (digit) {
memmove(s10 + 1, s10, len + 1);
*s10 = digit % 10 + '0';
digit /= 10;
len++;
}
s16++;
}
return s10;
}
int main(void) {
char s10[100];
puts(baseNtobase10(s10, "123", 10));
puts(baseNtobase10(s10, "123", 16));
puts(baseNtobase10(s10, "1234567890123456789012345678", 16));
return 0;
}
Output
123
291
369229998778771388878179932591736
I was going to suggest strtoll, but that only goes to 16 characters or 64 bits. I suggest you use the bigint library, which has way higher limits.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int myatoi(const char* string) {
int i = 0;
while (*string) {
i = (i << 3) + (i<<1) + (*string -'0');
string++;
}
return i;
}
void decimal2binary(char *decimal, int *binary) {
decimal = malloc(sizeof(char) * 32);
long int dec = myatoi(decimal);
long int fraction;
long int remainder;
long int factor = 1;
long int fractionfactor = .1;
long int wholenum;
long int bin;
long int onechecker;
wholenum = (int) dec;
fraction = dec - wholenum;
while (wholenum != 0 ) {
remainder = wholenum % 2; // get remainder
bin = bin + remainder * factor; // store the binary as you get remainder
wholenum /= 2; // divide by 2
factor *= 10; // times by 10 so it goes to the next digit
}
long int binaryfrac = 0;
int i;
for (i = 0; i < 10; i++) {
fraction *= 2; // times by two first
onechecker = fraction; // onechecker is for checking if greater than one
binaryfrac += fractionfactor * onechecker; // store into binary as you go
if (onechecker == 1) {
fraction -= onechecker; // if greater than 1 subtract the 1
}
fractionfactor /= 10;
}
bin += binaryfrac;
*binary = bin;
free(decimal);
}
int main(int argc, char **argv) {
char *data;
data = malloc(sizeof(char) * 32);
int datai = 1;
if (argc != 4) {
printf("invalid number of arguments\n");
return 1;
}
if (strcmp(argv[1], "-d")) {
if (strcmp(argv[3], "-b")) {
decimal2binary(argv[2], &datai);
printf("output is : %d" , datai);
} else {
printf("invalid parameter");
}
} else {
printf("invalid parameter");
}
free(data);
return 0;
}
In this problem, myatoi works fine and the decimal2binary algorithm is correct, but every time I run the code it gives my output as 0. I do not know why. Is it a problem with pointers? I already set the address of variable data but the output still doesn't change.
./dec2bin "-d" "23" "-b"
The line:
long int fractionfactor = .1;
will set fractionfactor to 0 because the variable is defined as an integer. Try using a float or double instead.
Similarly,
long int dec = myatoi(decimal);
stores an integer value, so wholenum is unnecessary.
Instead of
i = (i << 3) + (i<<1) + (*string -'0');
the code will be much more readable as
i = i * 10 + (*string - '0');
and, with today's optimizing compilers, both versions will likely generate the same object code. In general, especially when your code isn't working, favor readability over optimization.
fraction *= 2; // times by two first
Comments like this, that simply translate code to English, are unnecessary unless you're using the language in an unusual way. You can assume the reader is familiar with the language; it's far more helpful to explain your reasoning instead.
Another coding tip: instead of writing
if (strcmp(argv[1], "-d")) {
if (strcmp(argv[3], "-b")) {
decimal2binary(argv[2], &datai);
printf("output is : %d" , datai);
} else {
printf("invalid parameter");
}
} else {
printf("invalid parameter");
}
you can refactor the nested if blocks to make them simpler and easier to understand. In general it's a good idea to check for error conditions early, to separate the error-checking from the core processing, and to explain errors as specifically as possible so the user will know how to correct them.
If you do this, it may also be easier to realize that both of the original conditions should be negated:
if (strcmp(argv[1], "-d") != 0) {
printf("Error: first parameter must be -d\n");
else if (strcmp(argv[3], "-b") != 0) {
printf("Error: third parameter must be -b\n");
} else {
decimal2binary(argv[2], &datai);
printf("Output is: %d\n" , datai);
}
void decimal2binary(char *decimal, int *binary) {
decimal = malloc(sizeof(char) * 32);
...
}
The above lines of code allocate a new block of memory to decimal, which will then no longer point to the input data. Then the line
long int dec = myatoi(decimal);
assigns the (random values in the) newly-allocated memory to dec.
So remove the line
decimal = malloc(sizeof(char) * 32);
and you will get the correct answer.
if(!strcmp(argv[3] , "-b"))
if(!strcmp(argv[3] , "-d"))
The result of the string compare function should be negated so that you can proceed. Else it will print invalid parameter. Because the strcmp returns '0' when the string is equal.
In the 'decimal2binary' function you are allocating a new memory block inside the function for the input parameter 'decimal',
decimal = malloc(sizeof(char) * 32);
This would actually overwrite your input parameter data.