The C standard 9899:2011 7.22.3 states that:
If the space cannot be allocated, a null pointer is returned. If the size of
the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.
Meaning that what is returned from malloc is implementation-defined behavior. You'll have to read your specific compiler documentation to see what it does in your case: return a null pointer or a non-zero pointer pointing at 0 bytes of data (likely a random garbage location).
In either case, the returned pointer is not pointing at a valid memory location. So if you attempt to write to it, you'll invoke undefined behavior. Meaning anything can happen: the program can crash & burn, or it can seem to work correctly and crash one month later, or anything else.
Why this gives me a compilation error: p = malloc(sizeof(char) * 2)
Because you are trying to compile C code with a C++ compiler. Don't do that. Some C++ compilers have an option you can set to compile with a C compiler instead.
Side note about an inconsistency in the C standard:
The standard Annex J actually list the above as unspecified behavior. I suspect this must be an error in Annex J, as the normative text cited above clearly states that this is implementation-defined. 7.22.3 is normative and Annex J is informative, so I would just ignore Annex J.