0

I am just starting to learn C. I have run into a problem. Actual code is given at the bottom.

I have a function that modifies a dynamically allocated string taken as input and returns the modified string:

I have a dynamically allocated string A and a pointer char *B in main().

I call Func() in main() with string A as an argument and assigns it to B:

Func(A) uses parts of the string to populate a dynamically allocated string, char * output (MemoryBlock1). I free char * input inside func(), which is the dynamically created string A in main(). I return char * output to pointer B. So that even though Func(A) ends and the pointer char * output is destroyed, I can access that MemoryBlock1 through B.

Then I call func() again.

This time with string B (MemoryBlock1) as the input. It creates a new dynamically allocated string char * output a second time (MemoryBlock2) using parts from B (MemoryBlock1). I free char * input, which in this instance is B (MemoryBlock1), which was dynamically allocated and assigned to B in the last Func() call. B is now pointing to a freed memory. But in the next line I return output (MemoryBlock2) back to B and B is now pointing to an active memory again. Again, even though Func(B) ends and the pointer char * output is destroyed, I should have access to MemoryBlock2 through B.

I print B onto the screen.

However, The program output on the screen is blank.

If I omit freeing char * input in Func(), the program works. If I dont call B = Func(B); the program works.

In short, either I have to create strings C, D, E etc in main() every time I want to modify a previously modified string:

OR

I dont free input / B and keep on leaking memory from every B that becomes input and is left without a pointer when I reassign B to output, whenever I call B = func(B);

Any advice on how I can to make this work without additional pointers or memory leaks? Thank you.

// ACTUAL CODE
// To simplify this example, I am just reducing the string instead of modifying it.
// But everything else is the same

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *Func(char *input, int n) {

    char *output = (char *)malloc(n * sizeof(char));

    // populate output with parts from input.
 
    for (int i = 0; i < n; i++)
    {
        output[i] = input[i];
    }
    output[n] = '\0'; 
    // output is now a valid string.

    free(input); 
    // PROBLEM AREA 1
    // If this command is omitted, the program works fine even with PROBLEM AREA 2.

    return output;
}

int main(int argc, const char *argv[]) {

    char *A = (char *)malloc(100 * sizeof(char));
    puts("Enter string:");
    gets(A);

    char *B;

    B = Func(A, 80);
    // B is now pointing to memory that was pointed to by output in this Func(A) call.

    B = Func(B, 77);
    B = Func(B, 62);
    // PROBLEM AREA 2
    // B should point to memory pointed by new output in Func(B) call. Earlier memory of B should have been
    // freed by the free(input) command within Func(). 
    // But B appears to be blank, I suspect due to free(input) within Func().
    // If I don't call multiple times, program works fine. Even with PROBLEM AREA 1.

    printf("%s\n", B); // print whatever string is at B to screen.

    free(B); 
    B = NULL;
    //Since B is pointing to dynamic memory and I no longer need B.

    return 0;
}

Expected outcome: B should have been printed the reduced string to screen regardless how many time I call B = Func(B,n); where n is reduced each call.

Actual outcome: Program ends with a blank, new line. No erors are shown during compilation. It just runs and prompts a new command line at the terminal.

NOTE Program doesn't work ONLY IF both PROBLEM AREAS are in effect. If either one of them is ommited, program works fine although it with memory leaks.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 5
    `output[n] = '\0'; ` will write out of bounds and lead to *undefined behavior*. – Some programmer dude Mar 13 '23 at 08:21
  • 4
    And never ***ever*** use `gets`! It's so [dangerous](https://stackoverflow.com/questions/1694036/why-is-the-gets-function-so-dangerous-that-it-should-not-be-used) it has even been removed from the C language. Any learning material that teaches it should be thrown away. Use e.g. [`fgets`](https://en.cppreference.com/w/c/io/fgets) instead. – Some programmer dude Mar 13 '23 at 08:22
  • 1
    Also you [don't have to (and really shouldn't) cast the result of `malloc`](https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc/605858), or any function returning `void *`. – Some programmer dude Mar 13 '23 at 08:23
  • Does this answer your question? [Malloc to allocate the exact size of a string](https://stackoverflow.com/questions/66501909/malloc-to-allocate-the-exact-size-of-a-string) – Ken Y-N Mar 13 '23 at 08:26
  • @Someprogrammerdude Hi. Thank you for the tips. Even after rectifying output[n] = '\0' issue, the output is still blank. If i dont use free(input), it works but with memory leaks. The issue (what I think)really is that I am not able to free the pointer B and repoint it to the new memory block. within the function. – Kr9 Vishwa Mar 13 '23 at 08:44

1 Answers1

0
  1. Do not cast results of malloc (if program is not compiling then you use C++ compiler to compile C code and it is not right)
  2. Use the correct type for sizes
  3. Check if functions did not fail and check if given string is not shorter then you think it is.
  4. Do not use gets function
char * Func(char * input, size_t n)
{
    
    char *output;

    if(input)
    {
        int fillgap = 0;
        output = malloc((n + 1) * sizeof(*output));
        if(output)
        {
            for(size_t i=0; i < n; i++)
            {
                if(fillgap) output[i] = '_';
                else if(!(output[i] = input[i])) {fillgap = 1; i--;};
            }
            output[n] = '\0'; 
        }
    }
    free(input); 
    return output;
}


int main(int argc, const char * argv[])
{
    char *A = malloc(100 * sizeof(*A));
    char * B;

    if(A)
    {
        puts("Enter string:");
        fgets(A, 100, stdin);


        B = Func(A, 80);   
        if(B) printf("%s\n", B); // print whatever string is at B to screen.
        B = Func(B, 77);
        if(B) printf("%s\n", B); // print whatever string is at B to screen.
        B = Func(B, 62);
        if(B) printf("%s\n", B); // print whatever string is at B to screen.
    }
    free (B); 
    return 0;
}
0___________
  • 60,014
  • 4
  • 34
  • 74
  • Thank you all. It turns out the problem was caused because I was doing pointer arithmetic on the input pointer. It was implemented badly, as in it was permanently moving input[0] forward. It is working now. The handover of the memory blocks between pointers were not the issue. I appreciate all the tips and guides. – Kr9 Vishwa Mar 13 '23 at 10:40
  • @Kr9Vishwa you did not do any pointer arithmetics in the code you show. Does that mean the code you show us is not causing your problem at all? That would be a no-go. – Gerhardh Mar 13 '23 at 12:20
  • @Gerhardh I am sorry for the confusion. As I mentioned in the problem code, comment line 2; for simplicity, I had reduced the code. In actual program, I had used a while loop, instead of for loop, and was doing while(input += i) instead of while(input +i). So free(input) would have been called on a null character after loop exit. I did not see it & misjudged that the error was caused by the free command or some other error in memory allocation. Hence I posted it here for clarification. I regret any misunderstanding I have caused. – Kr9 Vishwa Mar 15 '23 at 07:26
  • @Kr9Vishwa that is why you are supposed to provide a [MCVE](https://stackoverflow.com/help/mcve). That is, some reduced code which you have verified to reproduce the problem. See [How-to-ask](https://stackoverflow.com/help/how-to-ask) for more hints. If your code does not contain the error, we will never be able to find it. – Gerhardh Mar 15 '23 at 07:30