작성
·
282
1
답변 2
4
"주소값의 최소 단위는 4byte라고 강의에서 배웠는데요. 이게 말하자면 4byte 간격으로 데이터를 저장할 수 있다는 의미이잖아요." 라고 말씀하셨는데 아니에요!!!!!!!!! 4byte 간격으로 데이터를 저장할 수 있다고 설명하셨는데 이건 주소값이 4byte 라는 것과 상관 없구요, 이건 오히려 int 배열에 대한 설명입니다. int 나 float 는 그 데이터 크기 자체가 4byte 이기 때문에 원소들이 당연히 4byte 간격으로 "저장"이 되겠죠? 주소값이 4byte 라는건 말그대로 집주소가 4byte 라는거에요. 집 주소를 표현하는데에 32bit 즉, 32자리의 비트를 쓴다는 의미입니다. "저장"과는 상관 없어요!! 집 자체가 4byte 간격으로 위치해 있다는 것이 아닌, "집 주소"가 4byte(32자리) 라는거에요! 즉 데이터가 위치한 그 "주소"가 4byte 크기의 숫자라는 것입니다. 32bit 환경은 주소값이 4byte 이며(즉, 메모리 주소가 32자리) 컴퓨터가 4byte 씩 정보를 읽어들이는 것을 의미해요. 64bit 완경은 주소값이 8byte 이며 컴퓨터가 8byte 씩 정보를 읽어들이는 환경을 의미합니다.
포인터 + 2, 포인터 + 1 할 때 8byte, 4byte 씩 더해지는 것 또한 32bit, 64bit 환경 시스템과 전혀 상관이 없구요, 그 포인터가 가리키는 "데이터의 크기"를 고려한 것입니다. int * ptr 은 int 데이터를 가리키는 포인터인데 int 는 4byte 메모리를 차지하는 놈이니(메모리 1칸을 1byte라고 치면 4칸 차지하는 셈) 당연히 다음 주소는 + 4 로 처리해야겠죠? +1 +2 +3 은 어차피 본인이 가리키고 있는 int 데이터 일부나 마찬가지니까요. 그래서 자동으로 +1 은 +4 로 해주는거나 마찬가지입니다. 오해하고 계신거에요! char * ptr 의 크기는 4byte 겠죠? (32bit 환경에서) 왜냐하면 주소값들은 4byte 크기니까요. 그러나 이것과는 전혀 상관없이 ptr + 1 은 +1byte 한거나 마찬가지입니다. 왜냐하면 char 데이터의 크기가 1byte니까요. 이처럼 포인터 산술 연산은 그 데이터 크기와 관련이 있는거지 주소값이 4byte, 8byte 인 그런 시스템적 환경과는 전혀 상관이 없습니다.
그리고 왜 오류가 뜨는지를 질문주셨는데, 질문자님의 에레메세지를 검색해보니 버퍼 오버플로우와 관련된 에러더라구요. 그걸 바탕으로 에러가 난 이유에 대해 추측해보자면., 현재 질문자님께서는 ptr 이 &ch + 1 주소를 참조하게끔 하습니다. 그러나 어떤 주소의 메모리를 쓰라고 정해주는건 개발자가 할 수 있는 일이 아닌, 운영체제 권한입니다. ch 의 메모리는 우리가 int ch 라는 변수를 선언해주었기에 운영체제로부터 허가받고 할당받은 메모리입니다. 그러나 &ch + 1 주소에 해당하는 메모리는 운영체제가 우리에게 쓰라고 부여해준 메모리 영역은 아닌 것입니다. 그런데 맘대로 *ptr = 22 하고 간접참조하여 그 메모리의 값을 바꿔버리는 조작을 하게 되면 뭐 작동 자체는 불가능한건 아니나 권한이 없는 메모리를 조작하게 된 것이기 때문에 문제가 생긴 것이죠. 쉽게 예시를 들어보자면, 배열을 7 크기로 선언했는데 우리가 8번째 원소에 어떤 값을 저장하려고 시도한다면 에러가 나겠죠? 물리적인 메모리 공간 상으로 8 번째 메모리에 접근할 수는 있지만 우리는 7 개의 메모리만 사용하도록 할당받았고 그 이외의 메모리를 사용하는건 권한이 없다는 의미나 마찬가지인거잖아요? 이런 에러와 비슷하다고 생각하시면 될 것 같아요. &ch 도 아니고 &ch + 1 는 운영체제가 우리에게 할당해준 메모리 공간이 아닙니다. 그러한 공간을 간접참조하여 조작하려는 행위는 마치 배열의 크기를 벗어난 메모리에 접근한 것과 같은게 아닐까 싶습니다. 마치 int* ptr = 1234567; 이렇게 아무 주소값 부여해놓고 *ptr = 22; 하여 그러한 메모리를 조작하려는 시도와도 비슷하다고 보시면 될 것 같아요.
반면 위와 같이 arr + 1 주소에 해당하는 메모리 공간(arr의 두번째원소)은 arr 을 통해 운영체제로부터 직접 할당받은 메모리인거죠?(2 크기의 배열이니까요) 그렇기 때문에 이땐 arr + 1을 ptr 에 넣고 간접참조 하고 값을 바꿔도 아무런 런타임 에러가 발생하지 않았습니다. 운영체제로부터 할당 받은 스택 메모리를 조작하는 것이니까요!
배열이 아닌 이상 변수 하나의 포인터에다 연산을 해주고 간접참조하는건 좀 위험하지 않을까 싶어요..! 위에서 말씀드렸다시피 운영체제가 부여해주지 않은 스택 메모리 주소에 함부로 접근하는거나 마찬가지니까요.
그리고 또 32bit, 64bit 환경에 따라 다른 결과가 나온다고 말씀하셨는데 이 부분에 대해서는 https://www.inflearn.com/questions/138582 이 링크로 대신하겠습니다. 환경에 따라 다른 결과가 나오는 이유에 대해 해당 링크에서 그림으로 자세히 설명을 드린적이 있기에 이거 보시고 이해해보셨으면 좋겠습니다.
코드를 제가 타이핑해서 실행해봐야해서 다음에 질문 주실 땐 캡처사진보단 전체 코드 복사 붙여넣기 해주시면 감사하겠습니다. 😀 그리고 질문이 여러개일 땐 분리하고 정리해서 써주시면 제가 이해하기가 더 수월합니다ㅠㅜ
0
안녕하세요 조준수님.
.
포인터 변수 선언시 int* ptr = malloc(sizeof(int)); 형식으로 힙에서 메모리를 얻어온 다음
ptr = &ch; 를 해보시겠어요?
스택 메모리를 메모리주소 연산으로 이용하면 크래시 위험이 있을것 같습니다.
malloc 함수를 이용하기 위해선 #include <stdlib.h> 를 첫줄에 추가하셔야 합니다.
.
감사합니다.