int *p1; // p1 contains a garbage value
*p1 = &v1; // store address of v1 in the location which is pointed to by p1
// since p1 is not initialised with a valid address,
// that also tries to use an invalid pointer
You don't need to dereference the pointer with an * when assigning a new value:
1 2
int *p1 = nullptr; // assign initial value
p1 = &v1; // store address of v1 in the pointer p1 (replaces nullptr)
The way to look at this is that the type is int* (a pointer to int) and the variable is p. You may adjust the spacing to emphasise that meaning int* p1 though the compiler doesn't see the difference).