문자열과 배열
문자열과 배열의 관계
C++의 '문자열'은 배열과 깊은 관계를 가지고 있다. 문자열이란 "Hello"와 같은 문자의 나열이다. C++에서는 이와 같은 영문자의 문자열을 'char형의 배열'로 취급할 수 있는 구조로 되어 있다. 'char형'은 변수 파트에서도 소개한 것처럼 '문자'를 나타내는 형이다. 문자열은 문자의 나열로 되어 있기 때문에 char형의 배열로 취급할 수 있다.
예를 들면, 상기의 "Hello"라는 문자열은 다음과 같이 취급한다.
char str[6]; //형의 배열을 선언한다.
str[0] = 'H';
str[1] = 'e';
str[2] = 'l';
str[3] = 'l';
str[4] = 'o';
str[5] = '\0'; //마지막에 '\0'을 붙인다.
char형의 배열을 선언한 후, 각 요소에 1개씩 문자를 대입한다. 이것으로 배열 str[]로 "Hello"라는 문자열을 취급하게 되는 것이다.
이 배열의 마지막에 대입하는 '\0'은 NULL 문자(NULL character)라고 한다. C++에서는 문자열 배열의 마지막을 나타내기 위해 반드시 마지막에 '\0'이라는 값을 붙이는 것으로 되어 있다. 이 '\0'을 잊어버리지 말아야 한다. 특히 배열을 선언할 때에는 주의가 필요하다.
예를 들면, "Hello"와 같이 5 문자로 되는 문자열을 취급하는 경우를 생각해 보기로 한다. 이 경우 5개의 문자로 구성되는 문자열이므로 요소를 5개밖에 가지지 않는 배열에서는 문자열을 취급할 수 없다. 이때 배열 요소는 최저 6개를 준비해야 한다. 최저 '문자열의 길이 + 1'의 요소가 필요한 것이다.
문자열 배열의 초기화
문자열은 다음과 같이 초기화하여 char형의 배열로 작성할 수 있다.
char str[6] = { 'H', 'e', 'l', 'l', 'o', '\0' };
char str[] = { 'H', 'e', 'l', 'l', 'o', '\0' };
char str[6] = "Hello";
char str[] = "Hello";
이들은 어느 것이나 "Hello"라는 문자열을 취급하기 위해 char형의 배열 str[6]을 초기화한 것이다. 문자열의 경우는 통상의 배열의 초기화 방법 외에 상기와 같이 " "로 둘러싸서 사용할 수 있다.
초기화한 경우는 자동적으로 NULL 문자(\0)가 추가된다. 또한 " "를 사용하여 대입하는 방법은 초기화할 때에만 사용할 수 있다. 다음과 같은 경우는 " "를 사용해서 문자열을 대입할 수 없기 때문에 주의해야 한다.
char str[6] = "Hello"; //" "를 사용하여 초기화할 수 있지만
/* 오류 */
/* str = "Hello" */ //" "를 사용해서 대입할 수 없음
문자열 배열 출력
이제는 배열에 저장한 문자열을 출력해 보는 것으로 한다. 우선 다음과 같은 코드를 입력하도록 한다.
예제) 문자열 배열 출력
문자열을 배열로 초기화하고 출력하는 코드를 작성해 보도록 한다.
#include <iostream>
using namespace std;
int main()
{
char str[] = "Hello";
cout << str << '\n';
return 0;
}
문자열을 포인터로 취급
문자열은 배열 대신에 char형으로 포인터를 사용해서 취급할 수도 있다. 즉, 다음의 코드와 같이 문자열을 취급할 수 있다.
const char *str = "Hello";
이 경우의 str은 char형으로의 포인터를 나타내고 있다는 것에 주의해야 한다. 포인터로 취급할 경우에는 " "를 사용함으로써 메모리 상의 어딘가 다른 장소에 문자열이 저장되고 그 장소를 나타내도록 되어 있다.
문자열을 char형으로의 포인터로 취급한 경우에도 문자열을 출력할 수 있다.
예제) 포인터와 문자열
포인터 변수를 사용하여 문자열을 출력하는 코드를 작성해 보도록 한다.
#include <iostream>
using namespace std;
int main()
{
const char* str = "Hello";
cout << str << '\n';
return 0;
}
실행 결과는 앞선 예제와 같다.
// (참고) char*를 사용할 경우 C언어 컴파일러와 구버전 C++ 컴파일러에서는 오류가 발생하지 않지만, 최근 컴파일러(VS 등)에는 오류가 발생한다. 사실 char* 형식의 포인터는 올바른 형식이 아니다. '문자열(string)'은 중간에 변경이 불가능한 '상수(const)'로 취급하므로 그런 의미에서 'const'를 붙여주는 것이 맞는 표현이다. 이전에는 개발자들이 편리하게 사용할 수 있도록 char* 방식의 포인터에도 저장할 수 있도록 했다. 하지만 이렇게 char*로 만들어낸 데이터를 중간에 바꿀 경우, 메모리 쓰기 접근 위반이 발생한다. 컴파일 시에는 오류가 표시 안되고, 실행 시 오류가 표시된다. 이것을 막고자 char* 앞에 const를 붙여야 컴파일 시 오류가 안 나도록 바뀌었다.
출처: https://blog.naver.com/dlscjs8646/222166081854
배열과 포인터의 차이
문자열을 배열로 취급할 경우와 포인터로 취급할 경우에는 몇 가지 주의가 필요하다. 포인터에 문자열(의 첫 번째 요소)을 " "로 사용해서 저장하는 경우는 배열에 저장한 경우와 다르고, 더욱이 " "를 사용해서 취급하는 문자열을 변경할 수 있도록 되어 있다. (배열에 저장한 경우 불가능)
const char *str = "Hello"; //""를 사용해서 포인터를 문자열로 초기화할 수 있다.
str = "Goodbye"; //""를 사용해서 포인터가 가리키는 문자열을 변경할 수 있다.
이 코드에서는 1행째에 str이 "Hello"를 나타내도록 하고 있다. 2행째에서는 str은 "Goodbye"를 나타내고 있다. 포인터의 경우는 어딘가 다른 장소에 저장된 문자열을 나타내고 있으므로 포인터가 나타내는 장소를 변경하는 것만으로 취급하는 문자열을 변경할 수 있도록 되어 있다.
// (참고) 본래 상수(const)는 중간에 값을 대입할 수 없지만, "포인터"의 관점에서 장소를 변경하는 것은 가능
또한 여기서는 출력 방법에 관해 설명했지만 문자열을 입력하는 경우에는 배열을 선언하고 문자열을 저장하는 영역을 정확하게 확보해두지 않으면 안 된다.
char str[100]; //문자열을 저장하는 배열을 준비
...
cin >> str; //배열에 문자열을 저장
문자열은 배열과 포인터의 2종류로 취급할 수 있지만 구조의 차이에 주의해 가면서 코드를 작성해야 한다.
문자열 처리
지금은 문자열이 배열인 것을 이용해가면서 문자열을 처리하는 코드를 작성하는 것으로 한다. 다음의 코드는 배열 str[ ]에 기억한 문자열 "Hello"의 각 문자 사이에 * 기호를 삽입해서 출력한다.
예제) 배열로 문자열 처리
배열 str[ ]에 기억한 문자열 "Hello"의 각 문자 사이에 * 기호를 삽입해서 출력하는 코드를 작성해 보도록 한다.
#include <iostream>
using namespace std;
int main()
{
char str[] = "Hello";
cout << "Hello\n";
for (int i = 0; str[i] != '\0'; i++) {
cout << str[i] << '*';
}
//이 코드에서는 for문을 사용해서 문자열 배열의 요소를 1개씩 취급한다.
cout << '\n';
return 0;
}
C++의 문자열은 반드시 '\0'으로 종료하기 때문에 배열 str[ ]의 요소에 '\0'가 나올 때까지 *를 삽입해 가면서 배열 요소를 반복해서 출력할 수 있다.
for (int i = 0; str[i] != '\0'; i++) { //'\0'이 아닌 동안 반복한다.
cout << str[i] << '*';
}
이와 같이 문자열을 처리하는 코드의 경우는 맨 마지막의 문자가 '\0'인 것을 이용한다.
문자열을 처리하는 표준 라이브러리 함수
지금까지 문자열의 처리 방법을 몇 개 살펴보았다. C++의 환경에는 문자열을 취급하기 위한 표준적인 함수가 첨부되어 있다.
이 함수 군을 표준 라이브러리(standard library)라고 한다. 표준 라이브러리에는 여러 가지 문자열 조작 함수가 정의되어 있다. 이것을 코드 내에 이용하면 문자열의 길이를 조사하는 작업과 복사를 수행하는 작업이 간단하게 수행하도록 되어 있다. 표준 라이브러리의 주요한 문자열 처리 함수는 다음과 같다.
size_t strlen(const char *s); |
문자열의 NULL 문자를 제거한 길이를 반환한다. |
char *strcpy(const char *s1, const char *s2); |
문자열 s2를 문자열 s1에 복사해서 s1에 반환한다. |
char *strcat(const char *s1, const char *s2); |
문자열 s2를 문자열 s1의 끝에 추가해서 s1을 반환한다. |
int strcmp(const char *s1, const char *s2); |
문자열 s1과 문자열 s2를 비교하고 문자열 s1이 문자열 s2보다 작을 때 : 음의 값 문자열 s1이 문자열 s2와 같을 때 : 0 문자열 s1이 문자열 s2보다 클 때 : 양의 값을 반환한다. |
표준 라이브러리 함수를 이용하려면 각 함수의 기능이 제공되어 있는 파일을 인크루드 한다. 문자열 조작 함수를 이용하려면 #include <string>으로 기술한다.
그러면 이 중의 strlen() 함수를 이용하여 문자열의 길이를 구하는 코드를 입력해 보도록 한다.
예제) strlen() 함수의 사용
표준 라이브러리인 strlen() 함수를 사용하여 문자열의 길이를 출력하는 코드를 작성해 보도록 한다.
#include <iostream>
#include <string> //문자열 조작 함수를 제공하는 표준 라이브러리를 취급하도록 한다.
using namespace std;
int main()
{
char str[100];
cout << "문자열 (영숫자)를 입력하시오.\n";
cin >> str;
cout << "문자열의 길이는 " << strlen(str) << "입니다.\n";
return 0;
}
이와 같이 표준 라이브러리 strlen() 함수를 사용하면 문자열의 길이를 조사할 수 있다. strlen() 함수로 반환되는 문자열의 길이란 '\0'을 포함하지 않고 문자만의 개수이다는 것에 주의해야 한다.
문자열을 배열의 복사
문자열을 배열에 복사하는 표준 라이브러리 함수를 사용할 수 있다. 다음의 코드를 살펴보도록 한다.
예제) strcpy() 함수의 사용
표준 라이브러리인 strcpy() 함수를 사용하여 문자열을 복사하는 코드를 작성해 보도록 한다.
#include <iostream>
#include <string> //문자열 조작 함수를 제공하는 표준 라이브러리를 취급하도록 한다.
using namespace std;
int main()
{
char str0[20];
char str1[10];
char str2[10];
strcpy_s(str1, "Hello"); //str1에 "Hello"를 복사한다.
strcpy_s(str2, "Goodbye"); //str2에 "Goodbye"를 복사한다.
strcpy_s(str0, str1);
strcat_s(str0, str2); //str0[]의 끝에 str2[]를 추가한다.
cout << "배열 str1은 " << str1 << "입니다.\n";
cout << "배열 str2는 " << str2 << "입니다.\n";
cout << "연결하면 " << str0 << "입니다.\n";
return 0;
}
여기서는 문자열을 배열에 복사하는 strcpy() 함수를 사용하였다. 더구나 배열의 끝에 문자열을 추가하는 strcat() 함수도 사용하고 있다. 여기서는 str1[]에 "Hello"를, str2[]에 "Goodbye"를 복사(저장)하였다.
또 str0[]에 str1[]을 복사해서 str0[]에 "Hello"가 저장되도록 하였다. 마지막에는 str0[]의 끝에 "Goodbye"가 추가되도록 strcat() 함수를 사용한 것이다.
// (참고) error C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. 보안상의 이유로 strcpy_s 사용
배열의 크기에 주의
strcpy() 함수와 strcat() 함수를 사용할 경우에는 배열의 크기에 아주 주의를 해야 한다. 함수의 인수가 나타내는 배열의 크기가 충분하지 않으면, 이들의 함수는 배열의 요소를 건너뛰어 문자열을 복사해 버린다. 예를 들면, 앞선 예제의 경우에 str0[]의 요소가 10개인 것으로 해버린다.
int main()
{
char str0[10];
char str1[10];
char str2[10];
...
이 경우 최후에 strcat() 함수를 사용해서 "Goodbye"를 복사했을 때 배열의 최대 요소를 건너뛰어 문자열을 저장하는 조작을 수행하는 것으로 된다. 하지만 처음에 기술한 것처럼 배열의 요소를 건너뛰고 요소를 취급하는 처리를 해서는 안된다. 이와 같이 기술한 코드는 코드를 실행했을 때에 치명적인 오류가 발생하는 경우가 있다.
특히 문자열의 끝에 추가하는 strcat() 함수 등에서는 오류로 배열의 크기를 건너뛰는 상태가 된다. 크기에 여유가 있는 배열에 추가하도록 해야 한다.
출처: 박흥복, 서정희. 2015. C++ 프로그래밍 (초보자를 위한). 문운당
'개발 > C++' 카테고리의 다른 글
C++의 고급 기능2 (0) | 2022.04.11 |
---|---|
C++의 고급 기능 (0) | 2022.04.08 |
C++ 배열2 (0) | 2022.04.05 |
C++ 배열 (0) | 2022.04.04 |
C++ 포인터3 (0) | 2022.03.31 |