C++ STRCAT 구현부분 질문

조회수 119회

include

using namespace std;

class My_string

{

private:

char* str;

public:

My_string(char *a) { str = a; };

~My_string() { };

int my_strlen() {

int b = 0;

while (1) {

if (str[b] == NULL)

{

break;

        }

        b++;
    }
    return b;
};
void my_strcpy(char* a) {
    str = a;
};
void my_strcat(char* a) {
    char* idx;
    idx = str;
    char* b;
    b=a;

    while (*str !='\0')
    {
        str++;
    }
    while (*b!='\0')
    {
        *(str++) = *(b++);
    }
    *str = '\0';
    str = idx;
};
void print() {
    cout << str;
};

};

int main()

{

char lang[100];

for (int i = 0; i < 100; i++) { lang[i] = NULL; }

string comd;

cout << "First String : ";

cin >> lang;

class My_string My_comd(lang);

while (1) {

cout << "Please Enter Command(strlen,strcpy,strcat,print,quit) : ";

cin >> comd;

if (comd == "strlen") {

cout << "String length : " << My_comd.my_strlen() << endl;

continue; }

else if (comd == "strcpy") {

cin >> lang;

My_comd.my_strcpy(lang);

cout << "Strcpy is done" << endl;

continue;


    }


    else if (comd == "print") {


        cout << "Current String : ";


        My_comd.print();


        cout << endl;


        continue;
    }
    else if (comd == "strcat") {
        cin >> lang;
        My_comd.my_strcat(lang);
        cout << "strcat is done" << endl;
        continue;
    }
    else if (comd == "quit") {
        cout << "종료하겠습니다.";
        break;
    }
}
return 0;

} 이 코드에서 CLASS 안에있는 MY_STRCAT 멤버함수를 호출 할 때만 오류가 발생하는데 어떻게 고쳐야할까요? 구글링해도 문제를 잘 모르겠네요

1 답변

  • 왜 이런 일이 발생하나요?

    캐릭터 포인터(char *)는 문자열과 비슷해 보이지만 일반적인 다른 언어에서의 문자열을 생각한다면 오류가 마구마구 생길 수 있습니다.

    C/C++에서 포인터는 쉽게 말하면 메모리 상의 위치를 나타내는 int입니다. lang"shiftpsh"를 입력받은 상황에서 메모리 공간을 대략적으로 나타내 보면 이렇습니다.

    ... s h i f t p s h NULL NULL NULL ...
        ^
        lang
    

    이 때 My_string 생성자를 관찰해 보면 이렇습니다.

    private:
        char *str;
    public:
        My_string(char *a) { str = a; };
    

    앞서 char *메모리 상의 위치를 나타내는 int 같은 존재라고 언급했습니다. str = a를 하면 str에는 a메모리 상의 위치 정보가 저장됩니다.

    중요한 것은 위치 정보의 복사는 일어나지만 실제로 그 위치에 있는 문자열의 복사는 일어나지 않는다는 것입니다. 그래서 langstr은 같은 메모리 공간을 공유하게 됩니다. 메모리 공간은 이렇게 됩니다.

    ... s h i f t p s h NULL NULL NULL ...
        ^
        lang 이면서 동시에 My_comd의 str
    

    자, 이제 strcat을 실행하고 두 번째 문자열 "asdf"를 입력받습니다. 여기서 문제가 발생합니다. cin >> lang을 하고 있는데, 메모리 공간에서 strlang같은 메모리 공간을 공유하고 있다고 했습니다. lang을 다시 입력받으면 메모리 공간은 이렇게 됩니다.

    ... s h i f t p s h NULL NULL NULL ...
        ↓ lang에 "asdf" 입력
    ... a s d f NULL NULL NULL ...
        ^
        lang[0] 이면서 동시에 My_comd의 str[0]
    

    엇, str을 건드리지 않았는데 str도 덩달아 바뀌었습니다.

    my_strcpy 함수도 체크해 볼까요?

        void my_strcat(char *a) {
            char *idx;
            idx = str;
            char *b;
            b = a;
            while (*str != '\0') {
                str++;
            }
            while (*b != '\0') {
                *(str++) = *(b++);
            }
            *str = '\0';
            str = idx;
        };
    // ...
        My_comd.my_strcat(lang);
    

    일단 alang을 제공합니다. 앞서 언급한 것과 같이 alang의 메모리 상의 위치입니다. lang은 이미 str와 같은 메모리 위치를 공유한다고 했습니다. 따라서 이런 상황이 연출됩니다.

    ... a s d f NULL NULL NULL ...
        ^
        lang = str = idx = a = b
    

    자, 여기서 str만 최초의 \0이 등장할 때까지 증가시켜 봅시다.

    ... a s d f NULL NULL NULL ...
        ^       ^
        |       str
        |
        lang = idx = a = b
    

    이제 b도 최초의 \0이 등장할 때까지 증가시키는 과정을 관찰해 봅시다.

    ... a s d f NULL NULL NULL ...
        ^       ^
        b       str
    
        ↓ *(str++) = *(b++);
    
    ... a s d f a NULL NULL NULL ...
          ^       ^
          b       str
    
        ↓ *(str++) = *(b++);
    
    ... a s d f a s NULL NULL NULL ...
            ^       ^
            b       str
    
        ↓ *(str++) = *(b++);
    
    ... a s d f a s d NULL NULL NULL ...
              ^       ^
              b       str
    
        ↓ *(str++) = *(b++);
    
    ... a s d f a s d f NULL NULL NULL ...
                ^       ^
                b       str
    
        ↓ *(str++) = *(b++);
    

    의도치 않았지만 무한 루프가 실행되는 것을 알 수 있습니다. bstr이 초기에 같은 메모리 공간을 참조하고 있어서, 결국에는 같은 문자열에서 연산을 하고 있었기 때문입니다. 이 함수는 이렇게 계속 실행되다가 처음에 정해 준 lang의 크기(100)를 넘어가면 할당되지 않은 메모리 공간을 참조해 segmentation fault 날 것입니다.

    어떻게 해결하나요?

    malloc, new char[N] 등의 방법으로, 메모리 공간에 lang과 다른 독립된 공간을 마련해 줘야 합니다. "C/C++ 메모리 할당" 등의 키워드로 검색해 보세요. mallocnew의 간략한 활용법은 이렇습니다.

    char* str = (char*) malloc(N * sizeof(char));
    // or
    char* str = new char[N];
    

    둘 모두 \0을 포함해 길이 N의 문자열을 저장할 수 있는 새로운 메모리 공간을 할당합니다.

    • malloc은 C 함수로, 몇 바이트를 할당할지 명시해 줘야 합니다. 이 경우 N * sizeof(char)만큼을 할당하면 됩니다.
    • new는 C++ 키워드로, 몇 바이트를 할당할지 명시해 줄 필요는 없습니다.

    그리고 코드를 작성할 때 char *를 할당하는 것은 메모리 공간 상의 위치만을 복사한다는 것을 잊지 말아야 합니다. 따라서 문자열을 복사하고 싶다면 문자열 안에 있는 문자들을 하나 하나 복사해야 합니다. for-문을 돌려서 해결해도 되고, 과제에서 요구할 경우 그렇게 하는 게 맞겠으나, 이런 메모리 복사 작업은 굉장히 자주 일어나는 작업이고 최적화 여지가 있기 때문에 C의 memcpy, C++의 std::copy 등 미리 정의된 함수들을 사용할 수도 있겠습니다.

    newfor-루프를 활용하여 생성자를 고치면 이렇게 되겠습니다.

    private:
        char *str;
    public:
        My_string(char *a) {
            str = new char[100];
            for (int i = 0; i < 100; i++) {
                str[i] = a[i];
            }
        };
    
        ~My_string() {
            delete str;
        };
    

    my_strcat 등의 함수도 같은 방법으로 고칠 수 있겠습니다.

    • 진짜 정말 감사합니다 ! 천천히 읽어서 이해한 후 제 것으로 만들겠습니다 정말 감사합니다 코린이이 2023.1.13 17:57

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)