[C++] char 배열 크기를 초과하면 어떻게 되는지 알아보자

new Dean( );·2021년 5월 4일
1

알아보자

목록 보기
1/3

🕵️

뭐가 궁금한지?

char 배열이 가득 찼을 때 메모리 상에서 무슨 일이 일어나는지

왜 궁금한지?

알고리즘 문제풀이를 하던 중 sscanf로 string에 들어온 정보를 쪼개서 char배열(A, B)에 저장하는데, 자꾸 첫 번째 배열(A)의 처음 문자에 NULL 문자(\0)가 들어왔다. 근데 배열 A만 받으면 잘만 들어온다. 그러다 문득 두 번째 배열(B)의 크기가 문자열의 길이와 일치할 때만 NULL 문자가 들어왔다는 사실을 알게 되는데 ...

모든 배열을 받아올 때첫 번째 배열만 받아올 때

🤔 요상하다..!

다시 보니 배열 B의 크기가 8이었음! frontend (최대 8글자) + NULL 문자 (1) = 최소 9 여야 했다.

문자열은 NULL 문자(\0)가 나올 때까지 문자열로 인식함

배열의 크기를 작게 설정한 건 알겠는데, 왜 앞 배열(A)에 영향을 주는지 궁금해서 실험해봄!
뭔가 자리가 부족해서 다른 배열 자리를 차지한 것 같은데.. 왜 다음 배열도 아니고 이전 배열인지?!



🔬 실...험...

1) 두 배열을 선언하고, 두 번째 배열만 크기를 초과해서 넣어보기 (문제상황재현)

크기가 4인 배열 A, B를 선언하고, A배열엔 3개, B배열엔 5개를 넣어보았다.

    char A[4], B[4];

    A[0] = 'A';
    A[1] = 'B';
    A[2] = 'C';

    B[0] = 'a';
    B[1] = 'b';
    B[2] = 'c';
    B[3] = 'd';
    B[4] = 'e';

    printf("%s %s", A, B); // eBC abcdeBC


배열B의 크기가 초과되니까 먼저 선언된 배열A에 추가되는 모습!
이후 문자열 B는 널문자가 있는 A[3]이전까지가 된다.



2-1) 세 번째 배열을 만들면 거기로 추가되나?

char A[4], B[4], C[4];
// A 3개, B 5개, C 3개 문자 넣음


여전히 자신(B)의 직전에 선언된 배열(A)로 추가된다!

2-2) 세 번째 배열이 초과되면??


마찬가지로 자신(C)의 직전에 선언된 배열(B)로 추가된다.


널문자 나올 때까지 거슬러 올라가서 (C->B->A) 정의된다.

문자열 C[C] 1234 [B] 5bcd [A] eBC

2-3) C, B 둘다 초과시키기


이렇게 되는군... 2-1번과 2-2번의 합! 직전에 선언된 배열로 추가추가 ~



3) 배열의 주소값 확인하기

메모리 구조가 어떻길래 이렇게 되는지 궁금해서 배열의 주소값을 확인해 보았다. (이걸 제일 먼저 해봤어야..!)
배열의 주소값을 확인해 보았다

printf("%x %x %x", &A[0], &B[0], &C[0]);
// e57ad8cc e57ad8c8 e57ad8c4
ABC
e57ad8cce57ad8c8e57ad8c4

헐!!! C - B - A 순서로 차례대로 4byte씩(각 배열의 크기) 차이를 두고 있었다.
자리가 차면 먼저 선언된 배열로 굳이 이동해서 메모리를 빌려쓰는 게 아니라, 배열의 시작 주소부터 차례대로 값을 집어넣던 것 뿐이다. 그리고, 출력할 때는 널문자가 나올 때까지 차례대로 출력된 것 뿐

Stack 영역은 메모리의 가장 큰 주소값부터 거꾸로 할당 된다. Buffer overflow를 피하기 위해! (kernel의 영역이 변경될 여지를 줄이기 위해)

Memory View


Memory View 에서 데이터가 들어오는 모습을 확인해 보았다. (초록색하이라이트 - 원래 배열C의 메모리 영역)
순서대로~ 차례차례~



4) 첫 번째 배열(A)에서 초과되면?


일단은 A에게 할당된 공간(위 사진에서 초록색 하이라이트된 부분) 뒤에도 비어있었기 때문에 초과해서 넣은 문자 'E'가 메모리에 들어가긴 했다. 하지만 NULL 문자가 없기 때문에 A를 출력해보면 쓰레기값이 출력된다. (환경에 따라 오류가 발생할 수도)



🧑‍⚖️ 결론

  • char배열의 크기는 데이터의 크기 + 1 이어야 한다. (마지막은 NULL문자)
  • Stack영역에서 배열은 가장 큰 주소값부터 할당된다.
  • 데이터는 배열의 시작 주소(배열의 이름)부터 차례로 들어간다.
  • 이때, 배열의 크기를 초과하더라도 할당된 영역이 있으면(위의 1번같은 경우) 들어가긴 한다. (그래서 위 그림에서 B배열의 크기를 초과했지만 A배열 자리에 들어간 것. C배열도 마찬가지) 대신 NULL문자까지가 해당 문자열로 읽힌다.
    ⛔️ 근데 이렇게 하면 안 됨!! 에러만 안 날 뿐이지, 원하는 대로 동작하지 않을 가능성 99%
  • 할당된 영역이 초과되면 에러가 발생하거나 문자열을 출력할 때 쓰레기 값이 나온다.

👉 배열을 사용할 땐 크기를 제대로 할당하자

1개의 댓글

comment-user-thumbnail
2021년 10월 22일

잘 보고갑니다.

// character array 끝에 '\0' 을 %s (문자열)의 끝으로 인식하고
// 스택에 할당됩니다.
char charArray[4];
// str 자체는 스택에 잡히지만 문자열이 저장되는 주소는 힙
string str;
printf("%s %s\n", charArray, str)

character array 는 왠만하면 쓰지 않는게 정신건강에 좋겠네요.
https://m-falcon.tistory.com/124

답글 달기