csetmallocfreevon-neumann

Writing a Von Neumann Ordinal generator in C : Problem with malloc


I want to write a computer programme that will do the following things :


1a. It will make an array 3 characters long.

£££

2a. It will then initialize the array with the string "{_}" and will call the array zero.

0 = {_}

[The array is called 0 because it has 1 _ stick and because 2^0 = 1.]


1b. It will then ask the user to press Enter.

' '

2b. Once the user presses Enter it will lengthen the array such that its new length is one plus two times the previous length.

{_}££££

3b. It will then change the last '}' character into ',' .

{_,££££

4b. Make a side by side copy of the previous array.

{,{

5b. Put a '}' at the end of the array and call it one.

1 = {,{}}

[The array is called 1 because it has 2 _ sticks and because 2^1 = 2.]


It it then repeat the same process as in b so that the end result is -

5c.

2 = {,{},{,{}}}

[The array is called 2 because it has 4 _ sticks and because 2^2 = 4.]


So, this is my algorithm. I will produce the end results of a couple more iterations -

3 = {,{},{,{}},{,{},{,{}}}}

4 = {,{},{,{}},{,{},{,{}}},{,{},{,{}},{,{},{,{}}}}}

5= {,{},{,{}},{,{},{,{}}},{,{},{,{}},{,{},{,{}}}},{,{},{,{}},{,{},{,{}}},{,{},{,{}},{,{},{,{}}}}}}

And so on.



I wrote the following in order to get the aforementioned job done -

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

void print_vno ( int * pv_number , int * pv_length , char * pv_vno ) {
    printf ( "%d =\n" , *pv_number ) ;
    for ( int i = 0 ; i < *pv_length ; i++ )
    { printf ( "%c" , *( pv_vno + i ) ) ; }
    printf ( "\n" ) ;
}

void generate_vno ( int * gv_number , int * gv_length , char * gv_vno ) {
    *gv_number = *gv_number + 1 ;
    int old_length = *gv_length ;
    *gv_length = 2 * ( *gv_length ) + 1 ;
    gv_vno = realloc ( gv_vno , *gv_length ) ;

    for ( int i = 0 ; i < old_length ; i++ )
    { *( 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 ) ;
}

int main (  ) {
    int number = 0 , length = 3 ;
    char * vno = malloc ( length ) , zero [] = "{_}" ;

    for ( int i = 0 ; i < length ; i++ )
    { *( vno + i ) = zero [i] ; }
    print_vno ( &number , &length , vno ) ; 

    start :
    printf ( "\nPress Enter for successive Von Neumann Ordinals, " ) ;
    printf ( "or press X then Enter to exit.\n" ) ;
    char enter ;
    scanf ( "%c" , &enter ) ;
    if ( enter != 'x' )
    { generate_vno ( &number , &length , vno ) ;
    goto start ; }

    free ( vno ) ;
return 0 ; }


The problem I am having is as follows -

If I run the code here, it counts up to 10 then crashes and gives this error message- "** Process exited due to resource limitations **".

If I run the code here, it counts up to 4 then crashes and gives this error message- "free(): double free detected in tcache 2 Aborted".

I suspect there is something wrong with the usage of malloc, realloc and free.

My question is- how can I make it count up to 20?

Thank you.


Solution

  • #include <ctype.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    // no pointers unless needed:
    void print_vno(int pv_number, int pv_length, const char *pv_vno) {
        printf("%d =\n", pv_number);
        for (int i = 0; i < pv_length; i++) {
            putchar(pv_vno[i]);               // pv_vno[i] instead of *(pv_vno + i)
        }
        putchar('\n');
    }
    
    // `gv_vno` is now a `char**`:
    void generate_vno(int *gv_number, int *gv_length, char **gv_vno) {
        *gv_number = *gv_number + 1;
        int old_length = *gv_length;
        *gv_length = 2 * (*gv_length) + 1;
        char *new_nvo = realloc(*gv_vno, *gv_length);
        if (new_nvo == NULL) { // check if realloc succeeded
            perror("malloc");
            free(*gv_vno);
            exit(1);
        }
        *gv_vno = new_nvo;     // save the new pointer
    
        for (int i = 0; i < old_length; i++) {
            new_nvo[i + old_length] = new_nvo[i];
        }
        new_nvo[old_length - 1] = ',';
        new_nvo[*gv_length - 1] = '}';
    
        print_vno(*gv_number, *gv_length, new_nvo);
    }
    
    int main() {
        int length = 3;
        int number = 0;
        char zero[] = "{_}";
        char *vno = malloc(length);
        if (vno == NULL) return 1;
        memcpy(vno, zero, length);
        print_vno(number, length, vno);
    
        for (;;) { // loop instead of a label and goto:
            printf("\nPress Enter for successive Von Neumann Ordinals, ");
            printf("or press X then Enter to exit.\n");
            char enter;
            // check that scanf succeeds and allow both `x` and `X` to quit:
            if (scanf("%c", &enter) != 1 || tolower((unsigned char)enter) == 'x')
                break;
            generate_vno(&number, &length, &vno);
        }
        free(vno);
    }