As noted, the memory pointer value from the reallocation does not, in effect, leave the called function, and so subsequent calls to the "generate_vno" work until a new chunk of memory has been used for the reallocation. To illustrate, I took your code and added in some "printf" statements to identify the memory address values within the function call and the value back in the "main" function.
craig@Vera:~/C_Programs/Console/Ordinal/bin/Release$ ./Ordinal
0 = {_}
Press Enter for successive Von Neumann Ordinals, or press X then Enter to exit. g
Address of gv_vno before reallocation: 0x563678f272a0
Address of gv_vno after reallocation.: 0x563678f272a0
1 = {_,{_}}
Address of vno after function call...: 0x563678f272a0
Press Enter for successive Von Neumann Ordinals, or press X then Enter to exit. g
Address of gv_vno before reallocation: 0x563678f272a0
Address of gv_vno after reallocation.: 0x563678f272a0
2 = {_,{_},{_,{_}}}
Address of vno after function call...: 0x563678f272a0
Press Enter for successive Von Neumann Ordinals, or press X then Enter to exit. g
Address of gv_vno before reallocation: 0x563678f272a0
Address of gv_vno after reallocation.: 0x563678f27ae0 <----- Note the change to a new memory location
3 = {_,{_},{_,{_}},{_,{_},{_,{_}}}}
Address of vno after function call...: 0x563678f272a0 <----- However, the main function still refers to the original location
Press Enter for successive Von Neumann Ordinals, or press X then Enter to exit. g
Address of gv_vno before reallocation: 0x563678f272a0 <----- Thus when function called again, it attempts to free memory already freed
free(): double free detected in tcache 2
Aborted (core dumped)
As noted, after the third call, the memory location for the character array has changed, but this information does not get back to the "main" function. So then the subsequent fourth call to the "generate_vno" function fails upon the reallocation.
Noting that double pointer references are one solution, I will offer up another path to a solution of the memory update that has a bit of a "Python" flavor to it. Following is a refactored version of your program that returns the current memory value to the "main" function.
#include <stdio.h>
#include <stdlib.h>
void print_vno (int * pv_number, int * pv_length, char * pv_vno)
{
printf ("%d = ", *pv_number) ;
for ( int i = 0 ; i < *pv_length ; i++ )
printf ("%c", pv_vno[i]) ;
printf ("\n") ;
}
char * generate_vno (int * gv_number, int * gv_length, char * gv_vno)
{
int old_length = *gv_length ;
char temp[old_length]; /* Using a work array to assist in copying and expansion */
for (int i = 0; i < old_length; i++)
temp[i] = gv_vno[i];
*gv_number = *gv_number + 1;
*gv_length = 2 * old_length + 1 ;
free(gv_vno); /* Using free and malloc in lieu of realloc as a preference */
/* FYI - may want to test for NULL value */
gv_vno = malloc(*gv_length * sizeof(char));
for (int i = 0; i < old_length; i++)
{
gv_vno[i] = temp[i]; /* Using usual and customary character indices */
gv_vno[i + old_length] = gv_vno[i];
}
gv_vno[old_length - 1] = ',';
gv_vno[*gv_length - 1] = '}';
print_vno ( gv_number, gv_length, gv_vno);
return gv_vno; /* This gets the new memory location back to main */
}
int main (void)
{
int number = 0, length = 2 ; /* To acquire the noted pattern wanted */
char * vno, zero [] = "{}", enter ;
vno = malloc(length * sizeof(char)); /* Preference to highlight the allocation */
for (int i = 0 ; i < length ; i++) /* Doing a character copy in lieu of a strcpy */
vno[i] = zero[i];
print_vno (&number, &length, vno);
while (1) /* Using "while" loop in lieu of "goto" */
{
printf ("Press Enter for successive Von Neumann Ordinals, or press X and then Enter to exit.\n");
if (scanf (" %c", &enter) != 1 || (enter == 'x' || enter == 'X')) /* Refactored key entry */
break;
vno = generate_vno (&number, &length, vno); /* Get updated memory location of character array */
}
free (vno);
return 0;
}
The major item to point out is refactoring the "generate_vno" function to return the memory location value so that it can be used to update the pointer value in the "main" function. This is not to discount the usage of double pointers, but just to illustrate an alternative method that might be clearer to anybody needing to analyze the program. Also, though using pointer arithmetic to copy and/or update character array values is absolutely valid, I again just offer up the usage of indices for positional reference within the array being expanded and populated that is often utilized. And, as had been noted in the good comments, one usually wants to avoid the usage of "goto" for loop processing. Therefore, the refactored code utilizes a "while" loop with a breakout when necessary.
With those refactoring tweaks and a slight tweak to the initial character array value to more closely replicate the desired Von Neumann pattern, following was a retest showing the terminal output which allowed to continual expansion of the character array without the program falling over.
craig@Vera:~/C_Programs/Console/Ordinal/bin/Release$ ./Ordinal
0 = {}
Press Enter for successive Von Neumann Ordinals, or press X and then Enter to exit.
g
1 = {,{}}
Press Enter for successive Von Neumann Ordinals, or press X and then Enter to exit.
g
2 = {,{},{,{}}}
Press Enter for successive Von Neumann Ordinals, or press X and then Enter to exit.
g
3 = {,{},{,{}},{,{},{,{}}}}
Press Enter for successive Von Neumann Ordinals, or press X and then Enter to exit.
g
4 = {,{},{,{}},{,{},{,{}}},{,{},{,{}},{,{},{,{}}}}}
Press Enter for successive Von Neumann Ordinals, or press X and then Enter to exit.
g
5 = {,{},{,{}},{,{},{,{}}},{,{},{,{}},{,{},{,{}}}},{,{},{,{}},{,{},{,{}}},{,{},{,{}},{,{},{,{}}}}}}
Press Enter for successive Von Neumann Ordinals, or press X and then Enter to exit.
g
6 = {,{},{,{}},{,{},{,{}}},{,{},{,{}},{,{},{,{}}}},{,{},{,{}},{,{},{,{}}},{,{},{,{}},{,{},{,{}}}}},{,{},{,{}},{,{},{,{}}},{,{},{,{}},{,{},{,{}}}},{,{},{,{}},{,{},{,{}}},{,{},{,{}},{,{},{,{}}}}}}}
Press Enter for successive Von Neumann Ordinals, or press X and then Enter to exit.
x
Again, the main thrust of this answer is to provide some printed debug type output to illustrate what was happening in the original code and to provide another alternative in updating where in memory the character array was residing. As always, it is good to delve into "C" tutorials to get a better understanding of how items such as pointers work and the scope of information going to and coming from functions.
gv_vno = realloc ( gv_vno...whatever you assign togv_vnoin that function, will not be visible to the caller after the function returns.gototo construct loops. Do use a space before%cin thescanf()format string. Do check the return value fromscanf()— if it doesn't return1, you've got problems.scanf ( "%c" , &enter ) ;(e.g. tries to cancel withctrl + d[orctrl + zon windows])?goto; thewhileandfor(anddo…while) loops are usually adequate and orthodox indentation makes them easy to read. The leading blank in” %c”means that white space such as the newline after the first input is ignored. That is normally what you want.