#include <iostream>
using namespace std;
int main()
{
char my_str[] = "Hello, World!";
char *ptr = my_str;
cout << ptr << endl;
return 0;
}
해당 코드를 실행했을때 내 예상대로라면 char *ptr을 cout으로 출력하면 my_str의 메모리 주소가 출력될 것 이라고 생각했지만...
Hello, Worl!
이런 깜찍한 친구가 출력되었습니다.
내가 알고있던 지식과는 다른 출력에 잠깐 얼을 타다 조사를 시작했습니다.
일단 *ptr에 my_str의 메모리 주소가 저장되는 것이 맞았습니다.
문제의 발생 원인은 cout이 char*를 문자열로 해석하기 때문이라고 합니다.
"그럼 printf로 출력하면 어떻게 될까"
cout이 char*를 문자열로 인식해 그런 것이라면 printf로 출력하면 될 것이라는 단순한 생각을 이행해보았습니다.
#include <iostream>
using namespace std;
int main()
{
char my_str[] = "Hello, World!";
char *ptr = my_str;
cout << ptr << endl;
printf("%#x", ptr+1);
return 0;
}
출력결과는
Hello, World!
0x61fe0a
printf로 출력시 정상적으로 출력되는 모습을 보였습니다.
이유는 두 출력문의 포인터 해석의 차이 때문이였습니다.
printf 같은 경우에는 포인터를 메모리 주소로 해석하여 출력합니다. 너무 당연한 이야기지만 cout은 그러지 못했습니다.
cout같은 경우에는 포인터를 문자열로 해석합니다. 그렇기에 char*를 문자열로 해석해 이 포인터가 가리키는 문자열 전체를 출력하려 합니다. 즉 null이 나오기 전까지 말입니다.
다행히도 int*등 다른 타입의 포인터를 cout으로 출력하면 포인터가 가리키는 실제 값이 아니라 메모리 주소를 출력해줍니다.
왜 그런지 또 의문이 생기네요.
cout은 다양한 타입을 출력할 수 있도록 여러 버전의 << 연산자가 오버로딩 되어 있습니다. 이 덕분에 cout이 같은 << 연산자를 사용하더라도 다른 방식으로 처리할 수 있습니다!
여기서 잠깐! 오버로딩이란 하나의 이름을 가진 함수 또는 연산자가 서로 다른 방식으로 동작할 수 있도록 여러 번 정의하는 것을 뜻 합니다!
char*는 C 스타일 문자열을 가리키는 포인터로 간주됩니다.
C++에서는 cout의 << 연산자가 char*에 대해 오버로딩되어 있습니다. 이 이 경우 char*는 단순한 메모리 주소로 취급되는 것이 아니라, 해당 주소가 가리키는 문자열을 출력하는 것으로 해석됩니다. 이때 cout은 포인터가 가리키는 위치부터 널('\0') 문자를 만날 때까지 문자열을 출력합니다.
오 신기하네요~ 그럼 제가 아래 코드를 실행시키면 o, World! 표가 출력된다는 것 같군요... 한번 실행해볼까요?
#include <iostream>
using namespace std;
int main()
{
char my_str[] = "Hello, World!";
char *ptr = my_str;
cout << ptr + 4 << endl;
return 0;
}
o, World!
이번에는 다행히 예상대로 출력되었네요!
- printf와 cout의 포인터 해석법이 다른다.
- printf는 포인터를 메모리 주소로 해석한다.
- cout은 포인터를 문자열로 해석한다.
- 이유는 cout의
<<연산자가 오버로딩 되어있기 떄문이다. 해당 이유 때문에int*와char*를 다르게 해석합니다.char*같은 경우에는 c 스타일 문자열로 해석되어 null문자를 만날때까지 문자열을 출력합니다.
휴~ c++강의를 보던 중 배운 포인터가지고 놀아보다가 블로그 글까지 쓰게 되었네요. 그럼 저는 진도를 마저 빼보도록 하겠습니다. 글 읽어주셔서 감사드립니다!