-1

Here I'm trying to write a code to find the powers of 2 by manipulating a string. I know this can be done with some built in functions but I'm interested in doing this using string manipulation techniques.

The problem works fine till 276 and then I don't know why my program displays some unwanted characters. I don't understand why this happens exactly from the exponent 76 and before that I get the desired output.

Here is my code. Hope I can find what's going wrong with your help.


CODE:

Note: Here I use 4 functions

  1. int len() : to find length of string.
  2. void add() : to perform the operation to find power of 2.
  3. void rev() : displays string in reverse order, as its the desired output.
  4. int main()
    #include <stdio.h>
    #include <stdlib.h>

    char *a;
    int i;

    int len()
    {
        for(i=0;a[i]!='\0';i++);
        return i;
    }

    void rev()
    {
        printf("\n");
        for(i=len();i>=0;i--)
            printf("%c",a[i]);
        printf("\n-------------------\n");
    }

    void add()
    {
        int k,v=0;
        for(k=0;k<len();k++)
        {
            v=2*(((int)a[k])-48);
            a[k]=v+48;
        }
        for(k=0;k<len();k++)
        {
            if((int)a[k]>57)
            {
                if(k<(len()-1))
                {
                    a[k+1]+=1;
                    a[k]=((((int)a[k])-48)%10)+48;
                }//in if 1
                if(k==len()-1)
                {
                    realloc(a,(len()+1)*sizeof(char));
                    a[k+1]=49;
                    a[k]=((((int)a[k])-48)%10)+48;
                }//in if 2
            }//out if
        }//for
    }//add

    int main()
    {
        int j;
        a=(char *)calloc(1,sizeof(char));
        a[0]='1';
        for(j=1;j<=81;j++)
        {
            add();
            printf(" %d :\n",j);
            rev();
        }
        scanf("%d",&i);
        return 0;
    }

I don't understand what's going wrong... does realloc has a limitation is assigning memory or is it my system's fault?


EDIT:

As @DavidSchwartz suggested I must assign the newly returning pointer starting address to a the variable

a=realloc(a,(len()+1)*sizeof(char));

and I want to how can I effectively detect and avoid over flow.

Cherubim
  • 5,287
  • 3
  • 20
  • 37

3 Answers3

5

You ignore the return value of realloc and just continue to use the old block of memory that may or may not still be yours. Surely you meant:

        a = realloc(a,(len()+1)*sizeof(char));
David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • but isn't `a` already sent as an argument to get reassigned? – Cherubim Mar 28 '16 at 17:46
  • Moreover, by ignoring the return value of `realloc()` he cannot detect when it fails. – John Bollinger Mar 28 '16 at 17:47
  • 2
    @ch3rub7 The value of `a` is passed to tell `realloc` which block of memory needs to be reallocated. But you need to capture the address of the (possibly) new block because it may be different. – David Schwartz Mar 28 '16 at 17:47
  • from `man realloc`: "*The realloc() function returns a pointer to the newly allocated memory, which is suitably aligned for any kind of variable and **may be different from ptr**, or NULL if the request fails.*" – Eugene Sh. Mar 28 '16 at 17:48
  • @DavidSchwartz Thanks that explains well.. but I have one question. Why did I get the right answer for the first 76 iterations? does that mean that newly allocated `a` took back the same starting block? – Cherubim Mar 28 '16 at 17:54
  • @EugeneSh. How could I avid over flow in my program – Cherubim Mar 28 '16 at 17:58
  • 1
    @ch3rub7 Who knows? Maybe you actually corrupted other things that you just didn't use. It takes an awful lot of work to understand how and why buggy code fails or succeeds in particular ways. You might have to look at the generated assembly or look at the platform's memory management library. – David Schwartz Mar 28 '16 at 18:02
  • @ch3rub7 By careful programming, by sticking to guidelines and by using tools like valgrind ( Valgrind is the last in the list by purpose..) – Eugene Sh. Mar 28 '16 at 18:03
  • It is much easier to avoid these kinds of issues in C++. For example, in C++ you could use `std::string`. – David Schwartz Mar 28 '16 at 18:09
  • even after using `a=realloc(...)` my problem still persists – Cherubim Mar 28 '16 at 18:14
  • @ch3rub7 Perhaps because `a[something] = '\0';` never occurs? – chux - Reinstate Monica Mar 28 '16 at 18:39
  • **Bad advice** occasionally gets positive votes. Sure, this is a step in the right direction (sort of), but it's still not 100% spot on. – autistic Mar 29 '16 at 03:49
5

You need to assign '\0' to your array (within the bounds you've allocated).

Consider a hypothetical array of eggs stored in an egg carton. If you tell someone there's a sequence of eggs at the start of the carton that continues on until they reach a terminal egg (something that looks different to the other eggs), what happens when they don't find the terminal egg? Mind explosion is what happens. They'll probably yell at you and accuse you of acting stupid; the behaviour is undefined.

From the computers perspective, we should keep looking for the terminal egg, past the other end of the carton, onto the floor, out the door, onto the road and splat... we're hit by a car; the behaviour is undefined, yet again.

You need to put that terminating egg into the carton. Keep in mind this means you also need to make space for that terminating egg.


Store the return value of realloc into a temporary variable!

As a matter of fact, realloc does have limitations, as does the rest of C: The limitations mostly affect those who guess how to use it rather than reading (the manual), as you've observed. There are two points to take away from that manual:

  • It's possible that realloc might allocate a new object, with a different address, which it communicates via the return value.
  • When realloc fails, it'll return NULL. It's important not to charge on overflowing the array, and to note that the old pointer hasn't been freed; if you overwrite the old pointer value with the new (null) pointer value, you won't be able to free it... You'll have leaked memory.

In fact, you should (almost) always check the return value of every standard C function. If you don't know how, find the appropriate manual and read it. If you can't read the manual without being confused about everything from top to bottom, you need a better book because you haven't yet grasped the basics.

Here is how you should (almost) always use realloc:

void *temp = realloc(array, size);
if (temp == NULL) {
    /* XXX: Handle allocation failure */
}
array = temp;

How you recover from allocation failure is your decision, but what's important is that you can recover from allocation failure (and without simultaneously leaking memory). A simple solution to fill in the blanks (the XXX comment) in the above code would be:

void *temp = realloc(array, size);
if (temp == NULL) {
    free(array); // This'll stop valgrind from complaining about leaked memory
    exit(EXIT_FAILURE);
}
array = temp;

Is there any particular reason you aren't using strlen? Perhaps your book hasn't taught you about that yet.


((((int)a[k])-48)%10)+48;

What is this? Hasn't your book taught you that you can add and subtract character constants yet? This is much more portable and legible: (a[k] - '0') % 10 + '0'

On the topic of books, clearly yours isn't working out for you. I'm confident that you'll benefit from K&Rs "The C Programming Language, 2nd edition". Do the exercises as you come across them; don't skip them.


One more thing: Don't cast malloc, realloc or calloc

Community
  • 1
  • 1
autistic
  • 1
  • 3
  • 35
  • 80
  • 2
    Great answer. Also there is never any reason to multiply by `sizeof(char)` as it is ALWAYS `1` by definition. – John Hascall Mar 29 '16 at 04:36
  • Indeed, and while we're on the topic of sizes, `sizeof` results in a `size_t`, `strlen` returns `size_t` and so should `len`, `i`, and `k`... and the format specifier corresponding to `size_t` is `%zu`, not `%d`. – autistic Mar 29 '16 at 04:50
  • Thanks very much and its true that my book didn't taught me most of what you said and I'll definitely refer to the book you mentioned. – Cherubim Mar 29 '16 at 12:57
  • and regarding usage of `strlen()`, I deliberately avoided using it. – Cherubim Mar 29 '16 at 14:36
  • @ch3rub7 That's a shame. As software developers we're taught to reuse code unless there's a *really good reason* not to, and I can't see any good reason not to reuse the standard library. – autistic Mar 29 '16 at 14:40
  • thankyou @Seb I'll keep that in mind from now on, I 've just started to learn. – Cherubim Mar 29 '16 at 15:12
1

realloc does not modify the pointer you pass to it. Instead, it returns a new pointer, that MIGHT be the one you passed to it, if there is enough free space in the memory.

Instead of realloc(a,(len()+1)*sizeof(char)); you should use a = realloc(a,(len()+1)*sizeof(char));.

You can learn more about realloc here: http://www.cplusplus.com/reference/cstdlib/realloc/

Marandil
  • 1,042
  • 1
  • 15
  • 31
  • How can you tell whether or not it modifies the pointer you pass to it? Maybe it does, maybe it doesn't, but surely that's a `realloc` implementation detail. It could internally be implemented as `x = internal_realloc(x, new_size); return x;`. – David Schwartz Mar 28 '16 at 17:49
  • Indeed, `realloc()` *cannot* modify the caller's pointer to the original block. – John Bollinger Mar 28 '16 at 17:49
  • @DavidSchwartz, of course `realloc()` cannot modify the caller's pointer. C has only pass-by-value semantics. It can modify the block to which the pointer points, but not the caller's copy of the pointer. – John Bollinger Mar 28 '16 at 17:50
  • @JohnBollinger It cannot modify the caller's pointer. It can modify the pointer passed to it. The pointer passed to it is, of course, a copy of the caller's pointer. The answer has the logic completely backwards, suggesting a pass by physically const reference. – David Schwartz Mar 28 '16 at 17:50
  • @DavidSchwartz It could indeed be internally implemented as `return internal_realloc(...);` but then it'd be a wrapper, right? Look at the reference linked to in this answer; do you see how the examples in the actual reference use *two* declarations, i.e. one for the return value, the other for the argument? The `a = realloc(a, ...);` idiom is *mostly* wrong. That's where both your answer and Marandil's answer go wrong here. – autistic Sep 28 '18 at 11:43