A struct sockaddr should generally only be used as the base type for a pointer.  It is a structure intended to cover the common initial sequence of the members in the address family specific socket address types (struct sockaddr_un, struct sockaddr_in, struct sockaddr_in6 etc.)
The only member that you can rely on struct sockaddr having is a single sa_family_t, indicating the socket address family.  The idea is that to obtain a sort of polymorphism - you can have a function that can operate on several different socket address types:
void foo(struct sockaddr *sa)
{
    switch(sa->sin_family)
    {
    case AF_INET: {
        struct sockaddr_in *sa_in = (struct sockaddr_in *)sa;
        /* AF_INET processing */
    }
    case AF_UNIX: {
        struct sockaddr_un *sa_un = (struct sockaddr_un *)sa;
        /* AF_UNIX processing */
    }
/* ... */