Search code examples
ccharc-strings

GTK function call messes up completely unrelated char* value


I have similar code (but in a greater scope) in a GTK application:

#include <stdio.h>

struct TStruct {
    char *string;
}

void func(struct TStruct *tstruct) {
    char chara[8];
    sprintf(chara, "%.*s", 4, "testing");
    tstruct->string = chara;
}

int  main() {
    struct TStruct tstruct;
    tstruct.string = NULL;
    func(&tstruct);
    printf("%s", tstruct.string);
    return 0;
}

This snippet by itself works fine. However, in the GTK application, creating a GTK object anywhere after the snippet corrupts the value for tstruct.string. Why could that be? Is the above snippet not the best way to assign strings?

Changing tstruct->string = chara to tstruct->string = g_strdup(chara) fixes the issue. I can kind of guess that that was the problem, but can someone explain how that works?


Solution

  • You need to pay attention on what you are doing. You are allocating an array of char in the stack, since it is local to the func function, and them you are assigning it to a member of the struct that you are passing as a parameter. You have some options to solve your problem. One is to use an array of char instead of a pointer to char and copying the array entirely:

    #include <stdio.h>
    #include <string.h>
    
    #define STRING_SIZE 8
    
    struct TStruct {
        char string[STRING_SIZE];
    };
    
    void func(struct TStruct *tstruct) {
        char chara[STRING_SIZE];
        sprintf(chara, "%.*s", 4, "testing");
        strncpy( tstruct->string, chara, STRING_SIZE );
    }
    
    int main() {
        struct TStruct tstruct = {0};
        func(&tstruct);
        printf("%s", tstruct.string);
        return 0;
    }
    

    Another one is allocating the memory dinamically and them freeing it:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define STRING_SIZE 8
    
    struct TStruct {
        char *string;
    };
    
    void func(struct TStruct *tstruct) {
        char *chara = (char*) malloc( STRING_SIZE * sizeof(char) );
        sprintf(chara, "%.*s", 4, "testing");
        tstruct->string = chara;
    }
    
    void freeTStruct( struct TStruct *tstruct ) {
        free( tstruct->string );
    }
    
    int main() {
        struct TStruct tstruct = {0};
        func(&tstruct);
        printf("%s", tstruct.string);
        freeTStruct(&tstruct);
        return 0;
    }
    

    And finally, I would implement something like this:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct MyType {
        char *string;
        size_t stringLength;
    } MyType;
    
    MyType newMyType( const char* string ) {
    
        MyType new = {
            .string = NULL,
            .stringLength = strlen(string)
        };
    
        new.string = (char*) malloc( (new.stringLength+1) * sizeof(char) );
        strncpy( new.string, string, new.stringLength+1 );
    
        return new;
    
    }
    
    void freeMyType( MyType *myType ) {
        free( myType->string );
    }
    
    int main() {
        MyType foo = newMyType( "bar" );
        printf( "string: \"%s\", length: %d", foo.string, foo.stringLength );
        freeMyType( &foo );
        return 0;
    }