Both programs exhibit Undefined Behavior because p is not pointing at any valid memory, its value is uninitialized and thus indeterminate, so calling cpy() with p as the destination will write to random memory, if not just crash outright.
That being said, the reason why << &p works is because &p is the address of the p variable itself, which is a valid address. The type that &p returns is char**, which operator<< does not have a specific overload for, but it does have an overload for void*, which char** is implicitly convertible to. That overload simply prints out the address as-is.
The reason why << p does not work is because operator<< does have a specific overload for char*. That overload treats the address as the start of a C-style null-terminated string, and will print out characters starting at the address until a '\0' character is reached. But, since p does not point to a valid C string, the behavior is undefined.
To make both programs work correctly, you need to do this instead:
Program1:
void cpy(char* p, const char* q) {
while(*p++ = *q++);
}
int main() {
char buffer[10];
char* p = buffer;
const char* q = "Bhargava";
cpy(p, q);
std::cout << "p = " << &p << std::endl;
}
Program2:
void cpy(char* p, const char* q) {
while(*p++ = *q++);
}
int main() {
char buffer[10];
char* p = buffer;
const char* q = "Bhargava";
cpy(p, q);
std::cout << "p = " << p << std::endl;
}