Your analogy is broken: The fair comparison would be between Vec2D * and int (*)[100], i.e. pointers to the object. However, in your first piece of code, you are obtaining a pointer to a subobject using an unrelated language mechanism (array-to-pointer decay).
The following works as expected:
typedef int T[100];
T numbers;
T * p = &numbers;   // or: int (*p)[100] = &numbers;
typedef Vec2D T;
T vec;
T * p = &vec;       // or: Vec2D * p = &vec;
Pointers to subobjects can also be taken:
int * p_elem = &numbers[0];
int * v_emen = &vec.x;
The only special magic rule is that the array expression numbers can decay to the address of its first element, so &numbers[0] is the same as (the result of) numbers:
int * p_elem = numbers;  // same as &numbers[0]