Unfortunately, these structures don't work like that... What you'll find is that the struct sockaddr is a "base" definition that really only includes the socket family field, and some minimum space reservation - and then by typecasting, you're able to access the fields of another structure. As you're using IPv6, this means that you'll have a much longer address, and will need to allocate storage accordingly.
As the various addressing schemes use different information (e.g: address length), you need to make sure you allocate enough storage for what you're trying to use.
In your situation, the following would work:
struct sockaddr_in6 a;
memset(&(a->sin6_addr.s6_addr), 0, sizeof(a->sin6_addr.s6_addr));
printf("SIZEOF: %lu\n", sizeof(a->sin6_addr.s6_addr));
/* don't be afraid to clear the whole structure before filling it in too! */
memset(&a, 0, sizeof(a));
When you subsequently use it to connect / bind, then you'd typecast it there. When passing the structure to the kernel, you can almost think of it as passing a type-less pointer... the kernel knows you're using IPv6 (because you said so when calling socket(), and in the address family field), and therefore which type to use. The typecast here just keeps the compiler happy - you're still sharing the full IPv6 info, and the size of that structure.
int fd, ret;
fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
ret = connect(fd, (struct sockaddr *)&a, sizeof(a));
Think of structures as a template you can lay over memory - it's just an indicator of how to read the data that is held in memory... by casting a region of memory to another type doesn't cause the underlying "thing" to change shape at all.
If you're allocating the address on the heap (not stack), then when you call *alloc(), you need to give the correct size for the addressing you're using.
struct sockaddr_in6 *a;
a = malloc(sizeof *a);
a = malloc(sizeof (struct sockaddr_in6)); /* this works too... */
a = malloc(sizeof (struct sockaddr)); /* this is BROKEN */