C code, rewritten to use a Person struct without pointers I'm new to C and I have been assigned the task of converting some code that uses structs with pointers to code that doesn't use pointers. I would be interested to know if the code I have written is 'good' code and how I could improve it. Here is the code I have written which creates a Person struct then creates a few sample persons.

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

struct person {
    char *name;
    int age;
    int height;
    int weight;
};

typedef struct person Who;

struct person Person_create(char *name, int age, int height, int weight) 
{

    Who w;

    w.name = strdup(name);
    w.age = age;
    w.height = height;
    w.weight = weight;

    return w;
}

void Print_person(Who w) 
{
    printf("Name: %s\n", w.name);
    printf("\tAge: %d\n", w.age);
    printf("\tHeight: %d\n", w.height);
    printf("\tWeight: %d\n", w.weight);
}


int main(int argc, char *argv[])
{
    struct person ryan = Person_create("Ryan McGovern", 37, 182, 67);
    struct person sandy = Person_create("Sandy McGovern", 32, 167, 49);

    printf("Ryan is at memory location: %p\n", &amp;ryan);    
    Print_person(ryan);

    printf("Sandy is at memory location: %p\n", &amp;sandy);
    Print_person(sandy);

    ryan.age += 2;
    ryan.weight += 4;

    Print_person(ryan);

    return 0;
}


Prints out:



Ryan is at memory location: 0x7fff52b7ea68
Name: Ryan McGovern
    Age: 37
    Height: 182
    Weight: 67
Sandy is at memory location: 0x7fff52b7ea50
Name: Sandy McGovern
    Age: 32
    Height: 167
    Weight: 49
Name: Ryan McGovern
    Age: 39
    Height: 182
    Weight: 71




Original code using pointers (from the book Learn C the Hard Way by Z. A. Shaw):


#include &lt;stdio.h&gt;
#include &lt;assert.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

struct Person {
    char *name;
    int age;
    int height;
    int weight;
};

struct Person *Person_create(char *name, int age, int height, int weight)

{

    struct Person *who = malloc(sizeof(struct Person));
    assert(who != NULL);

    who-&gt;name = strdup(name);
    who-&gt;age = age;
    who-&gt;height = height;
    who-&gt;weight = weight;

    return who;
}

void Person_destroy(struct Person *who)
{
    assert(who != NULL);
    free(who-&gt;name);
    free(who);
}

void Person_print(struct Person *who)
{
    printf("Name: %s\n", who-&gt;name);
    printf("\tAge: %d\n", who-&gt;age);
    printf("\tHeight: %d\n", who-&gt;height);
    printf("\tWeight: %d\n", who-&gt;weight);
}

int main(int argc, char *argv[])
{
    struct Person *ryan = Person_create("Ryan McGovern", 37, 182, 67);

    struct Person *sandy = Person_create("Sandy McGovern", 32, 167, 48);


    // print them out and where they are in memory 
    printf("Ryan is located at memory location %p:\n", ryan);
    Person_print(ryan);

    printf("Sandy is at memory location %p:\n", sandy);
    Person_print(sandy);

    // Make them age 20 years and print them out again
    ryan-&gt;age += 20;
    ryan-&gt;height -= 2;
    ryan-&gt;weight += 4;
    Person_print(ryan);

    sandy-&gt;age += 20;
    sandy-&gt;weight += 10;
    Person_print(sandy);

    // Clean up
    Person_destroy(ryan);
    Person_destroy(sandy);

    return 0;
}
This is an interesting problem. Removal of pointers can improve the safety of code. It's much more difficult in C than something like C++, though, as C doesn't have many other options like references or smart pointers. Regardless, here are some thoughts on your code.
Use Your typedefs
You've made a type definition for struct person named Who. But you only appear to be using it two of the five places where you need them. Many C programmers dislike using typedef with a struct. I am not one of those programmers. I think they make the code much nicer and easier to understand. You've made the typedef, so you should use it everywhere. Person_create() should return a Who not a struct person. Also in main() you should be creating 2 Whos, not 2 struct persons.
Removing pointers
You've said the goal was to remove pointers from your struct, but you're still using one for the name field. Why? You could make it a fixed-length array if you truly want to remove all use of pointers. You'll need to ensure that you don't copy anything too long into it, in that case, though. You could define a constant for the maximum length of a name in that case. Something like this:
const size_t kMaxNameBytes = 32;    // Maximum number of bytes including NUL terminator

struct person {
    char name [ kMaxNameBytes ];
    int age;
    int height;
    int weight;
};

typedef struct person Who;

Now you have a struct that has no pointers. It is bigger, however. The disadvantage with having no pointers is that every struct must carry all of its data within the struct leading to much larger pieces of data on the stack. If you want an array of thousands of persons, it may be too large to fit on the stack.
As mentioned above, you'll also need to be more careful with your copies of the name field, now, too. Instead of just calling w.name = strdup(name);, you'll have to do something like this:
size_t name_len = MIN(strlen(name), kMaxNameBytes - 1);
memcpy(w.name, name, name_len);
w.name [ name_len + 1 ] = '\0';

Note all the extra work you now have to do to ensure that the string doesn't go over the maximum length and that it is also properly NUL terminated. On the other hand, copying a whole person is easier because you don't have to manually copy the name string like you do in your version. You can simply memcpy() the bytes from one struct to the other because it's self-contained.
Handling Failure
Note that your code doesn't handle the failure of strdup(). If strdup() can't allocate the memory, it will return NULL. Your code doesn't check the return value and will happily set the name to NULL and keep going. This is likely to cause a memory error somewhere else in your program.