본문

[질문] calloc 함수를 이용한 메모리 할당

일시 추천 조회 702 댓글수 11


1

댓글 11

- 이전에 char형에 256 이상 입력하면 숫자가 다른게 튀어나와서 그걸 포인터에도 기대하고 메모리 할당도 1byte만 했는데 데, 포인터는 왜...되는걸까요?T..T 메모리 할당을 1byte만 하셨지만 입력한 값이 1000000이 나오는 것은, 그 1byte를 쓰고 계시지 않기 때문입니다. 메모리를 할당한 후에 p에 n의 포인터를 입력하셨으니, calloc의 타입을 char*로 바꾸던 int*로 바꾸던 아무런 의미가 없습니다. p는 이미 calloc에 대해서는 잊어버린 상태입니다. 그리고 scanf로 n에 입력하셨고, printf는 p가 가리키는 주소의 값, 즉 n의 값을 출력하게 됩니다. calloc으로 할당한 메모리를 쓰지 않고 계시므로, 이 프로그램은 calloc을 쓰던 말던 같은 동작을 합니다. 그림을 그리면 설명하기 쉬운데, 글로 쓰자니 알기 쉽게 쓰기가 어렵네요... - 그리고 포인터 정의할때 형(int, char)은 그냥 포인터가 지시하는 값의 형을 프로그레머가 참고하라는 의미인지요? 음... 어떻게 보면 맞는 말이기도 합니다. 다만 포인터의 형을 아무렇게나 쓰면 절대 안되므로 오해하진 마시기 바랍니다. 실제로 포인터를 선언하셨으면 int* 던지 char* 던지 float* 던지 ruliweb* 던지 int***** 던지 동일하게 8byte입니다. 이걸 int*나 char*로 구분하는 것은 프로그래머와 컴파일러가 참고하기 위함이고, 그 포인터가 가리키는 위치에 실제로 int가 있는지 char가 있는지를 보장해주지는 않습니다. 컴파일러는 int*가 와야할 부분에 char*가 오면, char*를 암묵적으로 int*로 변환해 줄 수도 있고, char*를 int*로 변환할 수 없다고 에러를 낼 수도 있습니다. 코드 블럭은 char*를 int*로 알아서 바꿔주는 모양이지만, gcc는 'cannot convert ‘char*’ to ‘int*’ in assignment' 라는 에러가 발생합니다. p=(char*)calloc(1, 1); 에서는, 1. calloc은 void* 값을 주고, 2. (char*)를 붙여서 void*가 char*로 바뀌었고, 3. int*에 대입하면서 char*가 암묵적으로 int*로 바뀌었습니다. calloc이 1byte를 할당하든 100byte를 할당하든, p는 그 주소 값에 4byte의 int 값이 있는 것으로 받아들입니다. 다만 테스트하신 코드는 그 1byte에 아무것도 하지 않고 p에 n의 int 포인터를 입력하셨으니, 그 뒤의 scanf와 printf가 정상적으로 동작합니다. - p=(int*)calloc(1, 1);... 해도 잘 나옵니다. 우선 이 줄은 없어도 동일한 동작을 합니다. (int*)냐 (char*)냐가 중요한 것이 아니라, 이 줄 자체가 아무런 의미가 없습니다. 앞서 말씀드렸듯 p=&n;을 하면서 calloc에서 할당한 메모리의 주소는 잊어버리기 때문입니다. 이 다음에 scanf를 하든 printf를 하든 n과만 관계가 있지 calloc으로 할당한 메모리는 잊어버렸기 때문에 쓸 수도 없고, 에러를 낼 수도 없습니다. 그리고, p=(int*)calloc(1, 1); 와 p=(char*)calloc(1, 1); 는 동일합니다. 원본은 calloc이 반환하는 void*이고, p는 포인터를 입력하면 원본이 무엇이었든 반드시 int*로 받아들입니다.

plok | (IP보기클릭)220.117.***.*** | 21.12.26 00:31
plok

지금 p=&n;을 쓰셔서 calloc으로 할당한 1byte 메모리를 쓰고계시지 않지만, calloc의 1byte에 값을 입력하고 싶다면 int *p; p=(int*)calloc(1, 1); scanf("%d", p); printf("%d", *p); 라고 쓰셔야 합니다. 이 코드는 분명히 잘못된 코드이지만, 실제로는 동작할 수도 있고, 안 할수도 있습니다. 동작한다고 해도 문제가 있는 코드입니다. p=&n;을 삭제하고, scan에 p를 썼습니다. 이렇게 쓰면 p는 calloc에서 할당한 메모리의 위치에 1000000을 입력하게 됩니다. 이 경우, 할당받은 메모리는 1byte지만, scanf는 그 뒤에 있는 할당받지 않은 3byte까지 값을 입력합니다. 할당받지 않은 메모리에 값을 입력할 때의 동작은 알 수 없습니다. 컴파일러에 따라서, OS에 따라서 달라질 수 있습니다. 다만 할당받지 않은 메모리에 값을 썼는데도 '우연히' 문제가 없었다면, 남은 3byte를 합친 4byte에 1000000이라는 값이 입력됩니다. 동일하게 printf도, 4byte를 읽어서 값을 표시하려고 하고, '우연히' 문제가 없어서 1000000을 출력할 수 있습니다. scanf와 printf는 할당받지 않은 메모리를 이용해도 될까요? 절대 그렇지 않지만, scanf와 printf는 시킨 일을 했을 뿐입니다. 지정해준 주소의 메모리가 할당 됐는지 안 됐는지 scanf와 printf는 모릅니다.

plok | (IP보기클릭)220.117.***.*** | 21.12.26 00:38
plok

감사합니다. 고민거리를 던져준 답변 감사드립니다. 메리크리스마스.

인민배우 심영 | (IP보기클릭)59.16.***.*** | 21.12.26 00:49
인민배우 심영

int main() { char c1 = 0x00; char c2 = 0x00; char c3 = 0x00; char c4 = 0x00; int *p = (int*)&c1; printf("%x\n", &c1); printf("%x\n", &c2); printf("%x\n", &c3); printf("%x\n", &c4); *p = 0x41424344; printf("%c\n", c1); printf("%c\n", c2); printf("%c\n", c3); printf("%c\n", c4); return 0; } 혹시 이 코드를 한번 실행시켜서 결과를 알려주실 수 있을까요? 코드 블럭의 결과가 궁금합니다.

plok | (IP보기클릭)220.117.***.*** | 21.12.26 00:51
plok

인민배우 심영 | (IP보기클릭)59.16.***.*** | 21.12.26 00:58
인민배우 심영

오... 예상하던 결과와 다른 결과가 나왔군요. 하지만 좋습니다. 일단 이게 무슨 코드냐 하면, 4개의 char 변수를 선언하고, 1개의 int 포인터 변수를 선언하고, int 포인터에 c1의 포인터를 지정했습니다. 즉, p는 int*지만 char 값인 c1을 가리킵니다. 그리고 p가 가리키는 주소에 0x41424344를 입력했습니다. 이 값은 char로 치환하면 ABCD입니다. 우선 코드블럭의 결과를 봅니다. ----- 61fe17 <- c1의 값이 존재하는 메모리의 주소입니다. 61fe16 <- c2의 주소 61fe15 <- c3의 주소 61fe14 <- c4의 주소 D <-c1의 값입니다. <-c2의 값입니다. 문자열 끝을 나타내는 값이라, 글자가 표시되지 않습니다. <-c3의 값입니다. <-c4의 값입니다. ----- 주목할 부분은 주소의 값이 1씩 바뀌는 부분입니다. 메모리 상에서 char 변수들이 각 1byte씩 차지합니다. 다만 순서는 c4, c3, c2, c1이군요. 이 다음 p는 c1을 가리키도록 하고, ABCD를 입력했습니다. int인데 어떻게 ABCD냐 하면, 각 1byte씩 적절한 숫자를 입력했다는 정도로만 이해해 주세요... ABCD를 입력했지만, 실제로 메모리에 저장되는 순서는 반대이고, DCBA 순서대로 입력되어, c1에는 D가 입력됩니다. CBA는 그 다음 할당받지 않은 메모리에 입력되었습니다. 운 좋게 에러는 발생하지 않았군요. 다음 댓글에 제 결과를 쓰겠습니다.

plok | (IP보기클릭)220.117.***.*** | 21.12.26 01:11
인민배우 심영

제 gcc의 결과는 이렇습니다. ----- 1b335bf4 1b335bf5 1b335bf6 1b335bf7 D C B A ----- 출력된 내용은 아까와 같이 char 변수들의 주소와 입력된 값입니다. 주목할 부분은, 아까와 같이 char 변수들의 주소가 1씩 차이나지만, 순서는 반대라는 것입니다. 이 경우 메모리에는 c1, c2, c3, c4 순으로 1byte씩 차지합니다. 이 다음에 p는 c1을 가리키고 ABCD를 입력했습니다. 이 경우도 메모리에는 DCBA 순으로 입력됩니다. 그 결과 c1에는 D, c2에는 C, c3에는 B, c4에는 A가 입력되었습니다. 저는 c2 c3 c4는 0으로 초기화를 했지만, p가 가리키는 위치, 즉 c1을 수정했는데 c2 c3 c4도 바뀌어버렸습니다. 이와 같이, int*에 char*를 지정하여도 int*는 메모리 상의 4byte를 건드리게 되며, 이게 정상동작할지 아닐지는 알 수 없습니다.

plok | (IP보기클릭)220.117.***.*** | 21.12.26 01:18
plok

int가 4byte니까 1byte 씩 끊어 읽을때 각각 65,66,67,68이 나오도록 4byte에 맞춰 숫자를 입력하셨다는 의미로 이해했습니다.

인민배우 심영 | (IP보기클릭)59.16.***.*** | 21.12.26 01:19
plok

아하.... union에서 char하고 int하고 설정했을때 값이 막 중구난방으로 바뀌는 경우와 비슷한 거 같네요. 늦은 밤에 이렇게 장문의 답변을 달아주시고T..T 죄송합니다.

인민배우 심영 | (IP보기클릭)59.16.***.*** | 21.12.26 01:22
인민배우 심영

union도 마찬가지로, 결국 자기가 할당한 메모리를 int로 쓰느냐? char로 쓰느냐? 그건 자기가 코드를 짜기 나름입니다. 다만 union은 적어도 그 안에서는 할당된 메모리를 사용합니다. 그런데 calloc에서 1byte를 할당해서 그 주소를 받았는데, 나는 그 주소에 4byte를 억지로 쓰겠다? C라면 그렇게 할 수 있습니다. 다만 무슨 문제가 생길지 모르고, 위와 같이 다른 변수들을 모르는 사이에 수정해버릴 수도 있습니다. 실제로도 코드가 복잡해지면 내가 받은 포인터가 뭔지도 모르고 건드리다 에러가 나는 경우가 허다합니다. 결론적으로는, 코드가 동작하는 것 처럼 보여도 잘못된 코드일 수 있다는 점은 알아두세요.

plok | (IP보기클릭)220.117.***.*** | 21.12.26 01:38
인민배우 심영

여기까지 쓴 김에 한가지 더 씁니다. int main() { char c1 = 0x00; *(int*)&c1 = 0x41424344; // ABCD printf("c1 value: %c\n", c1); char c2; char c3; char c4; printf("c2 value: %c\n", c2); printf("c3 value: %c\n", c3); printf("c4 value: %c\n", c4); return 0; } 코드를 잘 보시면, c1을 선언하고 c1에 ABCD를 아까처럼 대입하고, 그 다음에 c2, c3, c4를 선언하였습니다. 결과는 아래와 같습니다. c1 value: D <- 이때, 아직 c2, c3, c4는 존재하지 않습니다. c2 value: C c3 value: B c4 value: A 놀랍게도, 있어선 안될 C B A 값이 이미 c2 c3 c4에 입력되어 있습니다. 보시다시피 c2 c3 c4는 선언하고 바로 printf로 출력하고 있습니다. 이것 또한 쓰레기 값입니다. c2가 만들어지기 전에 이미 메모리는 사용된 상태이고, '우연히' C라는 값이 입력된 위치에 c2가 메모리를 할당받은 것입니다. 이와 같이, 할당한 메모리 보다 더 큰 영역에 값을 쓰는 것은 잘 되는 것 처럼 보일수도 있으나, 분명히 잘못 쓴 코드입니다.

plok | (IP보기클릭)220.117.***.*** | 21.12.26 02:02
댓글 11
1
위로가기
세가좋어 | 추천 0 | 조회 293 | 날짜 2024.04.12
공돌이인생 | 추천 0 | 조회 105 | 날짜 2024.03.29
루리웹-1970043849 | 추천 0 | 조회 200 | 날짜 2023.12.18
질풍비장의패 | 추천 1 | 조회 589 | 날짜 2022.11.30
루리웹-3648796984 | 추천 0 | 조회 774 | 날짜 2022.11.23
루리웹-8641268934 | 추천 2 | 조회 700 | 날짜 2022.08.07
루리웹-4247021526 | 추천 2 | 조회 747 | 날짜 2022.07.25
루리웹-4746138650 | 추천 0 | 조회 676 | 날짜 2022.06.01
키라 요시카게 | 추천 1 | 조회 736 | 날짜 2022.05.24
우너무너 | 추천 0 | 조회 663 | 날짜 2022.05.14
키라 요시카게 | 추천 0 | 조회 646 | 날짜 2022.04.03
와이플 | 추천 0 | 조회 652 | 날짜 2022.01.14
와이플 | 추천 0 | 조회 670 | 날짜 2022.01.13
와이플 | 추천 1 | 조회 819 | 날짜 2022.01.13
인민배우 심영 | 추천 0 | 조회 879 | 날짜 2022.01.09
인민배우 심영 | 추천 0 | 조회 726 | 날짜 2022.01.08
오잇스 | 추천 0 | 조회 644 | 날짜 2022.01.05
인민배우 심영 | 추천 0 | 조회 538 | 날짜 2022.01.02
인민배우 심영 | 추천 0 | 조회 661 | 날짜 2021.12.31
인민배우 심영 | 추천 0 | 조회 1029 | 날짜 2021.12.30
인민배우 심영 | 추천 1 | 조회 650 | 날짜 2021.12.27
salvare545 | 추천 0 | 조회 611 | 날짜 2021.12.27
_ 식봉이는 식봉해 | 추천 4 | 조회 1175 | 날짜 2021.12.27
인민배우 심영 | 추천 0 | 조회 702 | 날짜 2021.12.25
인민배우 심영 | 추천 1 | 조회 812 | 날짜 2021.12.24
인민배우 심영 | 추천 0 | 조회 916 | 날짜 2021.12.16
V1046R-MAHORO | 추천 1 | 조회 507 | 날짜 2021.12.14
인민배우 심영 | 추천 0 | 조회 702 | 날짜 2021.12.10

1 2 3

글쓰기
게시판 매니저