문자열을 포인터와 연관 시키면 조금 복잡해진다.
char flower[20] = "rose";
cout << flower << "s are red" << endl;
실행 결과.
배열 이름은 첫 번째 원소의 주소이다. 그래서 널 문자를 만날 때까지 출력이 되었다. 그럼 오른쪽에 "s are red"는 뭘까? 문자열 출력에 일관성이 있으려면 큰 따옴표로 둘러싸인 문자열도 반드시 주소여야 한다. 배열과 마찬가지로, 문자열도 문자열의 첫 번째 문자의 주소를 나타낸다. 이걸 알면 char형의 포인터도 만들 수 있다.
const char* bird = "wren";
이렇게 하면 포인터 변수 bird에 "wren"의 주소가 저장된 것이다. '문자열 상수'이기 때문에 const를 썼다.
위의 예시에는 const char*을 이용했는데 단순히 char*로 하면 안된다.
이는 해결 방법이 2가지가 있는데, 첫 번째는 위에서처럼 const char*를 이용하는 것이고, 두 번째 방법은 프로젝트 → 속성 → C/C++ → 언어 → 준수 모드를 '아니오'로 해주는 것이다. 구버전에는 준수 모드가 자동으로 아니오로 돼있어서 표준 문법을 조금 무시(?)할 수 있었는데 요즘 버전은 준수 모드가 자동으로 '예'로 돼있어서 엄격하다고 한다.
근데 왜 안될까? 다른 자료형들과 달리, 문자열은 한번 정해지면 bird[3] 뭐 이런 식으로 변경이 불가능하다. 그래서 문법상 const가 맞다. 난 최신 버전에 따라서 그냥 const char*를 사용할란다.
char는 다른 자료형과 같은 방법으로 주소를 확인할 수 없다. 다음 코드를 보자.
char* p = new char[4];
char c = 'a';
char* pC = &c;
cout << "대입 전 주소들 c : " << &c << ", p : " << p << ", pC : " << pC << endl;
p = pC;
cout << "대입 후 주소들 c : " << &c << ", p : " << p << ", pC : " << pC << endl;
실행 결과.
이상하게 나온다. 왤까?
찾아보니 ostream을 사용할 때 char*를 주소가 아니라 문자열로 인식해서 널문자가 나올 때까지 출력한다 어쩌구 저쩌구 해서 저렇댄다... 이럴땐 강제 형변환을 해주면 된다.
char* p = new char[4];
char c = 'a';
char* pC = &c;
cout << "대입 전 주소들 c : " << (void*)&c << ", p : " << (void*)p << ", pC : " << (void*)pC << endl;
p = pC;
cout << "대입 후 주소들 c : " << (void*)&c << ", p : " << (void*)p << ", pC : " << (void*)pC << endl;
실행 결과.
잘 나온다~
new를 사용해서 구조체도 생성할 수 있다.
struct inflatable
{
char name[20];
float volume;
double price;
};
inflatable* ps = new inflatable;
ps->price = 20.0;
cout << "ps->volume = " << ps->volume << endl;
cout << "(*ps).volume = " << (*ps).volume << endl;
delete ps;
여기서 자료형은 struct이 아니라 구조체 이름을 사용한다. 동적 구조체는 포인터의 가리킨다는 의미를 받아들여 화살표 모양(->)을 쓴다. 보기 매우 불편한 구문이지만, (*ps).volume이랑 ps->volume은 같다. 가독성을 위해 나라면 후자를 사용하겠다.
new를 사용해서 동적 구조체를 배열로 만들고 싶다면, 배열처럼 [숫자]만 붙이면 된다. delete는 항상 유의하자.
inflatable* ps = new inflatable[5];
delete[] ps;