No, you can just use a pointer to struct S without ever defining the fields of S, no void pointers required.
What if you're providing an API header to an otherwise closed-source binary and you don't want to expose the struct definition?
How would the compiler know the size and alignment required for struct Foo if it never gets to see its definition?