본문 바로가기
C++/뇌를 자극하는 C++

[C++] 챕터 17 - 문자열 : 문자의 배열

by Minkyu Lee 2023. 6. 14.

// 17-1 : 문자열의 복사

#include <iostream>
#include <cstring> // 문자열 관련 함수 사용 준비한다. c는 c++에서 제공하는 함수라는 의미다.
using namespace std;

int main()
{
    char src[] = "Hanbit-Media"; // 문자열을 만든다.
    int len = strlen(src); // 문자열 길이를 잰다.

    char* dest = new char[len + 1]; // 새 문자열에 메모리 할당한다.
    strcpy(dest, src); // src 내용을 dest로 복사한다.

    // 출력
    cout << src << "\n";
    cout << dest << "\n";

    // 메모리 해제
    delete[] dest;
    dest = NULL;

    return 0;
}

/*
--- 결과 ---
Hanbit-Media
Hanbit-Media

---
C스타일과 C++스타일 문자열 사용법이 있다.
C++스타일은 문자열을 객체지향적으로 사용한다.

문자열의 사용은 크게 '저장'과 '처리'로 나눌 수 있다.

C스타일의 문자열은 '문자의 배열'에 보관한다.
사용은 C++ 제공하는 함수를 사용한다.

// strlen()
반환하는 길이는 NULL을 제외한 길이이다. 주의하라.

// strcpy()
뒤쪽의 문자열을 앞쪽의 문자열에 복사한다.
*/

 

// 17-2 : 결합한 문자열을 비교하기

#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    // 결합
    char str1[20] = "abcde";
    char str2[] = "fghij";
    strcat(str1, str2); // 두 문자열을 결합한다.

    // 비교
    if (strcmp(str1, "abcdefghij") == 0) // 같다면
        cout << "same\n";

    if (strcmp("123456", str1) != 0) // 다르다면
        cout << "not same\n";

    return 0;
}

/*
--- 결과 ---
same
not same

---
문자열이 같은지 비교한다.
또한 문자열 두 개를 하나로 합친다.

// strcat()
인자 두개를 받고 합쳐서 하나의 문자열을 만든다.

// strcmp()
두 문자열을 한글자씩 비교한다.
한 번이라도 같지 않다면 즉시 0이 '아닌' 값을 반환한다.
두 문자열이 다르다고 알린다.

*/

 

// 17-3 : C++에서의 char*의 의미

#include <iostream>
using namespace std;

int main()
{
    char c = 'A';
    char s[] = "This is a string.";
   
    cout << s << "\n";
    cout << &s[0] << "\n"; // 위와 동일하다.

    cout << &c << "\n"; // 문자의 주소를 넘겨준다.

    return 0;
}

/*
--- 결과 ---
This is a string.
This is a string.
A儆儆儆儆儆儆儆儆儆儆儆儆儆儆儆儆儆?his is a string.

---
문자열을 저장하기 위해서는 배열 사용 또는 동적 메모리 할당이 필요하다.

C++에서는 문자열의 첫번째 바이트의 주소를 사용해 전체 문자열을 지칭한다.

하지만 홀로 있는 char* 타입의 변수가 가리키는 곳에
char변수 하나가 있을지, 문자열이 있을지 구분할 수 없다.
이것이 변수의 주소인지 문자열의 첫번째 바이트 주소인지 애매하다.
그래서 함수 호출하는 사람이 볼 수 있께 주석을 달아놓는 것이 일반적이다.

하지만 대부분 char 변수 하나의 주소를 주고 받을 일은 거의 없어서 대부분 문자열이다.

그래서 cout 객체 역시 이러한 생각으로 만들어졌다.
char 변수의 주소를 넘겨주어도 문자열처럼 취급한다.
NULL 문자열이 나올때까지 모든 문자를 출력한다.

*/

 

// 17-4 : 문자열의 생성

#include <iostream>
#include <string> // c스타일은 cstring이지만, c++스타일은 string이다.
using namespace std;

int main()
{
    string s = "C++ style";
    cout << s << "\n";

    return 0;
}

/*
--- 결과 ---
C++ style

---

*/

 

// 17-5 : 문자열의 복사

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string src = "Hanbit-Media";
    string desc;
    desc = src; // 문자열 복사한다. 대입 연산자를 사용한다.
   
    cout << src << "\n";
    cout << desc << "\n";

    return 0;
}

/*
--- 결과 ---
Hanbit-Media
Hanbit-Media

---
C++스타일의 문자열 복사가 수행속도가 더 빠르지는 않다.
사용하기에 편한 것일 뿐이다.

*/

 

// 17-6 : 문자열의 길이

#include <iostream>
#include <string>
using namespace std;

int main()
{
    // 문자열 정의
    string s1;
    string s2 = "123";
    string s3 = "abcdefg";

    // 문자열 길이 출력
    cout << s1.size() << "\n";
    cout << s2.size() << "\n";
    cout << s3.size() << "\n";

    return 0;
}

/*
--- 결과 ---
0
3
7

---
객체 뒤에 점을 찍고 함수를 호출한다.

*/

 

// 17-7 : 문자열의 결합과 비교

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string str1 = "abcde";
    string str2 = "fghij";

    // 결합
    str1 = str1 + str2;

    // 비교
    if (str1 == "abcdefghij")
        cout << "same\n";

    if (str1 != "123456")
        cout << "different\n";

    return 0;
}

/*
--- 결과 ---
same
different

---

*/
 

 

// 17-8 : 문자열의 검색

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string text = "find test";

    // 위치 찾기
    cout << text.find("test");

    return 0;
}

/*
--- 결과 ---
5

---
오프셋을 반환한다.
오프셋이란 기준점으로부터의 떨어진 거리를 말한다.

strstr()함수도 있다.
차이점은 위치의 주소를 반환한다는 점이다.
*/

 

// 17-9 : 문자열의 일부분 얻기

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string path = "c:\\Document\\Pictures\\33.jpg"; // 역슬래시를 두 번 넣은 것에 주목하라.
    int len = path.size(); // 문자열의 길이 얻기
    string ext = path.substr(len - 3, 3); // 끝의 3바이트만 읽어온다.
   
    cout << ext << "\n";

    return 0;
}

/*
--- 결과 ---
jpg

---
substr 함수는 두개의 인자를 받는다.
인자를 순서대로 a,b 라고 할 때,
a번째 옵셋에 있는 문자부터 b개를 읽어오라는 뜻이다.
이렇게 읽어온 문자들을 별도의 문자열로 만들어 반환한다.
*/

 

// 17-10 : C스타일에서 C++ 스타일로의 형변환

#include <iostream>
#include <string>
using namespace std;

int main()
{
    char cstyle[] = "Are you a string, too?";

    // 변환
    string cppstyle;
    cppstyle = cstyle; // 대입연산자 사용하면 자동으로 변환된다.

    cout << cstyle << "\n";
    cout << cppstyle << "\n";

    return 0;
}

/*
--- 결과 ---
Are you a string, too?
Are you a string, too?

---
C스타일의 문자열은 암시적인 형변환을 통해 C++스타일 문자열로 쉽게 변경할 수 있다.
대입하면 알아서 변환한다.

*/

 

// 17-11 : C스타일에서 C++ 스타일로의 형변환 테스트

#include <iostream>
#include <string>
using namespace std;

int main()
{
    char cstyle[] = "Are you a string, too?";

    // 변환
    string cppstyle;
    cppstyle = cstyle; // 대입연산자 사용하면 자동으로 변환된다.

    // 글자 변경
    cppstyle[0] = 'B';

    cout << cstyle << "\n";
    cout << cppstyle << "\n";

    return 0;
}

/*
--- 결과 ---
Are you a string, too?
Bre you a string, too?

---
문자열이 복사된 곳의 문자를 일부 바꾸었다.
마치 배열 다루듯 접근하면 된다.

*/

 

// 17-12 : string 객체에서 C 스타일의 문자열 얻기

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string cppstyle = "Yes, I am";
   
    const char* cstyle = NULL; // C스타일 문자열을 가리킬 포인터를 준비한다.
    cstyle = cppstyle.c_str(); // C스타일 문자열의 주소를 반환한다. 이 주소에 포인터 변수 cstyle을 보관한다.

    cout << cstyle << "\n";
    cout << cppstyle << "\n";

    return 0;
}

/*
--- 결과 ---
Yes, I am
Yes, I am

---
c_str 함수는 문자열 주소를 반환한다.
이 문자열은 여전히 cppstyle의 소유이다.
또한 읽는 용도로만 사용할 수 있다.

즉, string 객체 안에 있는 문자열을 '읽기 용도'로만 사용하고 싶다면, c_str 함수를 사용한다.
*/

 

// 17-13 : C++스타일에서 C 스타일로의 변환

#include <iostream>
#include <string>
#include <cstring>
using namespace std;

int main()
{
    string cppstyle = "Yes, I am";

    // C스타일 문자열
    char* cstyle = new char[cppstyle.size() + 1]; // +1에 주의하라. NULL 문자열 담기 위해서다.
    strcpy(cstyle, cppstyle.c_str() ); // 문자열의 포인터를 얻어와서, strycpy 함수를 호출한다.
    cstyle[0] = 'Z'; // 첫번째 문자 변경

    cout << cstyle << "\n";
    cout << cppstyle << "\n";

    // 메모리 해제
    delete[] cstyle;
    cstyle = NULL;

    return 0;
}

/*
--- 결과 ---
Zes, I am
Yes, I am

---
완전한 C 스타일의 문자열의 복사본을 얻는 예이다.

// 복사 방법 정리
1. c_str 함수를 사용해 문자열의 포인터를 구한다. 이 포인터는 읽기 용도다.
2. 구한 포인터로 strcpy 함수를 호출한다. 복사본이 만들어진다.

*/

 

// 17-14 : 간단한 문자열의 입력

#include <iostream>
#include <string>
using namespace std;

int main()
{
    char cs[20]; // c 스타일
    string cpps; // c++ 스타일

    // 입력
    cin >> cs; // 심각한 문제점이 있다.
    cin >> cpps;

    // 출력
    cout << cs << "\n";
    cout << cpps << "\n";

    return 0;
}

/*
--- 결과 ---
test1 (입력)
test2 (입력)
test1
test2

---
// 11번째줄은 심각한 문제점이 있다.
크기를 넘어서는 문자열 입력시 엉뚱한 메모리 영역에 덮어써진다.
비정상적 종료 또는 엉뚱한 곳 코드가 실행된다.

위처럼 메모리 크기를 넘는 많은 양의 문자열 입력하는 해킹 방법을
버퍼 오버플로우 공격 이라고 한다.

// 한가지 문제점이 더 있다.
입력 받을 때, 공백을 포함한 문자열을 입력 받을 수 없다.
abc def 라고 입력시, 각각 2개를 입력 받았다고 인식한다.
*/

 

// 17-15 : getline() 함수를 사용한 문자열 입력

#include <iostream>
#include <string>
using namespace std;

int main()
{
    char cs[20]; // c 스타일
    string cpps; // c++ 스타일

    // 입력
    cin.getline(cs, 20); // getline 함수를 사용한다. c스타일과 c++스타일 사용법이 다르다.
    getline(cin, cpps);

    // 출력
    cout << cs << "\n";
    cout << cpps << "\n";

    return 0;
}

/*
--- 결과 ---
test1 test2 (입력)
test3 test4 (입력)
test1 test2
test3 test4

---
공백을 포함해 입력받을 수 있다.
getline함수를 사용하였기 때문이다.

// c++스타일의 사용법
평범한 함수다. cin 객체의 멤버가 아니다.

// c스타일의 사용법
cin 객체의 멤버 함수다.
할당된 메모리 크기만큼의 문자열을 받는다.
따라서 함수 사용하는 것이 더욱 안전하다.

*/

댓글