1.
vw_send((uint8_t *)&msg, strlen(&msg));
msg is an object, '&' returns the address of the object ( pointer ), then the pointer is cast to a uint8_t type pointer.
strlen must accept a pointer.
The second version:
the msg variable is already a pointer, it is just cast to the uint8_t type.
2.
char msg;
This creates an object of type char accessed by its alias msg
It will reside in the stack if used in a function ( automatic variable ), or in SRAM quota if in file or global scope.
char *msg;
This creates a pointer that 'can' point to a char object, however at creation it doesn't point to anything valid.
char *msg = new char[ 4 ];
This is creating the same pointer, but also assigning a newly allocated memory location to it.
Once your pointer has data, its current location can be accessed using the '*' dereference operator.
or as you can see the 'new' allocation creates 4 objects in an array.
Pointers and arrays are alike ( an array is a pointer managed by the system ).
*msg = 0; //Set first item to 0
msg[ 0 ] = 0 //Set first item to 0
*(msg + 1 ) = 5; //Set second item to 5
msg[ 1 ] = 5 //Set second item to 5
const char *msg;
This is a pointer to a constant character. You cannot use this pointer to change the value being pointed.
EDIT:
char msg;
char *msgptr = &msg;
This creates an object msg and sets its address as the pointer location for msgptr