// code readability is every bit as important as the algorithm used
// when compiling, have all warnings enabled, and then fix them
// OP can add parameter checking and make the str1 and str1 be pointers
//    with appropriate changes to the rest of the code
// I changed return types from void to int
// for set() and concat() so main could free the allocated memory areas
// the 'software contract' concept says the sub functions in the file
// do not need to check their parameters for validity
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // strlen()
// strdup() should have been prototyped by string.h,
// but was not on my computer
char *strdup(const char *s); 
// dont clutter the code with frivilous typedef's
struct string_t
{
    int    len;
    char * s;
};
// prototypes
int concat (struct string_t*, struct string_t* );
int set( struct string_t*, char* );
int main(void)
{
    //create variables
    // your learning C, so keep it simple
    struct string_t str1 = {0,NULL};  // good programming practice to initialize local variables
    struct string_t str2 = {0,NULL};
    if( !set( &str1, "hello " ) )
    {
        if( !set( &str2, "world!" ) )
        {
            printf("\nconcatenate str1 and str2\n");
            if( !concat(&str1,&str2) )
            {
                printf("concatenation result is:\n");
                printf("%p , %s",(void*)&(str1.s), str1.s);
                printf("\n------End------\n");
            } //  end if
        } // end if
    } // end if
    free(str1.s);
    free(str2.s);
    return EXIT_SUCCESS;
} // end function: main
// <-- pString1 must point to an instance of struct string_t
// do not free() the pNewString
// as it is a pointer to a literal,
// not a pointer to allocated memory
int set( struct string_t* pString1, char* pNewString ) // <-- use meaningful/descriptive names
{
    int returnValue = 0; // indicate success
    char * temp = strdup(pNewString);
    if( NULL == temp )
    { // then strdup failed
        perror( "strdup failed" );
        returnValue = 1; // indicate failure
    }
    else
    { // else, strdup successful
        pString1->s   = temp;
        pString1->len = strlen(pString1->s)+1;
    }
    return( returnValue );
} // end function: set
int concat (struct string_t* pString1, struct string_t* pString2)
{
    int returnValue = 0; // indicate success
    int totalLen = pString1->len + pString2->len + 1;
    //printf( "\nbefore: string1->len =%i,string2->len=%d, totalLength=%i\n",
    //            pString1->len,
    //            pString2->len,
    //            totalLen);
    //printf("\nbefore: string1:%s, string2:%s\n",
    //            pString1->s, pString2->s);
    // <-- there is no room in string1->s for any more chars so:
    char * temp;
    if( NULL == (temp = realloc(pString1->s, totalLen) ) )
    { // then realloc failed
        perror( "realloc failed" );
        returnValue = 1; // indicate failure
    }
    else
    {
        free( pString1->s);
        pString1->s = temp;
        //printf("\n after realloc: str1.len:%i, strl.s:%s\n",
        //       pString1->len, pString1->s);
        int i=0;
        for(;i<totalLen;i++)
        {
            pString1->s[strlen(pString1->s)] = pString2->s[i];
            pString1->s[strlen(pString1->s)] = '\0';
        } // end for
        pString1->len = totalLen;
        pString1->s[totalLen] = '\0';
        //printf("after: str1addr:%p , str1:%s\n",pString1->s,pString1->s);
        //printf( "\nstring1->len =%i,string2->len=%d, totalLength=%i\n",
        //        pString1->len,
        //        pString2->len,
        //        totalLen);
    } // end if
    return( returnValue );
} // end function: concat