안녕하세요
다름이 아니라 63.10에서
allocArray((void**)&numArr1, 10, sizeof(long long));
allocArray((void**)&numArr2, 3, sizeof(int));
numArr 앞에 (void**)가 들어간 이유가 뭔지 알려주실수 있을까요
빼고 실행시켜도 문제없이 실행되던데
제가 넣은 코드는 간단하게 메모리만 할당해줬습니다
void allocArray(void** Arry, int num, int size)
{
*Arry = malloc(size * num);
}
(void **)로 변환하지 않으면 오류가 발생합니다.
문제에서는 long long*이나 int*을 모두 받을 수 있도록 일반 포인터(void*)를 사용하는 함수를 작성하는 것을 요구합니다.
allocArray를 호출할 때 (void**)을 삭제하면
첫 번째 호출은 long long*이고
두 번째 호출은 int*이므로
둘 다 타입이 불일치하게 됩니다.
VC++에서는 이를 무시하고 컴파일을 하지만, clang, gcc 같은 표준에 좀 더 엄격한 컴파일러는 컴파일을 실패하고, 에러 메시지를 보여주게 됩니다.
/tmp/886245463/main.c:16:16: error: incompatible pointer types passing 'long long **' to parameter of type 'void **' [-Werror,-Wincompatible-pointer-types] allocArray(&numArr1, 10, sizeof(long long)); ^~~~~~~~ /tmp/886245463/main.c:6:24: note: passing argument to parameter 'Arry' here void allocArray(void** Arry, int num, int size) ^ /tmp/886245463/main.c:17:16: error: incompatible pointer types passing 'int **' to parameter of type 'void **' [-Werror,-Wincompatible-pointer-types] allocArray(&numArr2, 3, sizeof(int)); ^~~~~~~~ /tmp/886245463/main.c:6:24: note: passing argument to parameter 'Arry' here void allocArray(void** Arry, int num, int size) ^ 2 errors generated.
UNIT 63.2에 첫 번째 문장을 잘 보시면...
void 포인터 매개변수를 사용하면 자료형 변환을 하지 않아도 모든 자료형을 함수에 넣을 수 있습니다.
자료형 변환을 하지 않고, 모든 자료형을 함수에 전달하려면 어떻게 할까? void 포인터 매개변수를 써야 한다는 것입니다.
여기서 자료형 변환은 void 포인터 전환을 할 필요가 없다는 뜻이 아닙니다. void 포인터(일반 포인터)로 전환할 필요는 있습니다.
단, 함수 매개변수가 int로 선언되어 있어서 모든 값을 int로 변환하는 것을 할 필요가 없다는 뜻입니다.
안녕하세요
답글 감사드립니다
그런데 의문이 해결이 되지 않아서 다시한번 질문을 드립니다
제가입력한 코드는 하기와 같이 main함수에서 allocArray함수를 호출할때
하기와 같이 void** 로 호출이 되었습니다.
void allocArray(void** Arry, int num, int size)
*/ main함수에서 선언된 변수가 단일포인터(long long *numArr1) 임으로
이중포인터 (void **Arry) 로 입력 받음
(main함수 의 *numArr1의 메모리 위치를 읽기위한 역참조 **Arry) /*
{
*Arry = malloc(size * num);
*/ *Arry 는 main 함수의 단일포인터(long long *numArr1) 의 메모리 주소를
역참조하기 위하여 *Arry로 작성 /*
}
58.3 의 type_conversion_void_pointer.c 를 보게되면
int 형으로 형변환후 역참조를 하고 있습니다
{
int num1 = 10;
void *ptr;
printf("%d\n", *(int *)ptr);
}
상기의 코드를 참고하면 int 형으로 변환하기 위하여 *(int*)형으로 변환하는데
63.10의 경우는 이미 long long *numArr1; 과 같이 이미 포인터 변수로 선언을 했음
으로 이중포인터 자료형 변환 (*(void**)) 형태로 되어야 변환이 되는게 아닌가요?
포인터의 개념이 아직 잡히지 않아서 이상한 질문이 많지만 알려주시면
감사하겠습니다.
포인터는 두 가지 유형이 있습니다.
자료형에서 파생된 포인터가 있습니다.
int*, char* 같은 유형입니다.
타입은 반드시 일치해야 하고, 일치하지 않을 때는 형변환을 해서 일치시켜야 합니다.
그러나 타입에 상관없이 처리하는 함수가 필요할 때가 있습니다. 이럴 때는 일반 포인터로 전달하고, 함수 내부에서 원하는 타입으로 변환해서 처리하는 것입니다.
void*는 그런 용도로 사용하는 일반 포인터입니다. void는 타입이 없다는 뜻이므로 void*에 대해서는 어떤 연산도 하면 안 됩니다. 포인터의 전달에만 사용하고, 다시 원래의 유형으로 변환해서 사용합니다.
UNIT 58.3의 예는 void *ptr, 일반 포인터를 변환하는 예를 보여주는 것입니다.
ptr = &num1;
처럼 일반 포인터에 할당했으니 일반 포인터를 타입 포인터로 변환해야 합니다. 따라서 (int*)로 먼저 변환하고, 타입 포인터를 *로 역참조해서 가져오는 예제입니다.
UNIT 63.10을 보면...
타입으로 설명하지 말고, 타입을 풀어서 설명하면 이해가 더 쉽습니다.
long long *numArr1은 포인터이고, numArr1을 인수로 전달해서 메모리를 할당받아와야 합니다.
allocArray 함수는 int *numArr2도 인수로 받아서 메모리를 할당해야 합니다. 즉, 범용적인 메모리 할당 함수를 작성하라는 과제입니다.
따라서 일반 포인터로 변환해야 합니다.
(void*)numArr1으로 코드를 작성한다면
numArr1은 pointer to long long에서 pointer to void로 바뀝니다.
타입 자체가 바뀌죠.
(void**)numArr1으로 코드를 작성한다면 타입이 불일치합니다.
numArr1은 1단계 포인터이고, void**은 2단계 포인터이니 이를 일치시키기 위해
(void**)&numArr1으로 코드를 작성해서 2단계 포인터로 만들어서 함수로 전달합니다.
numArr1은 pointer to pointer to void로 바뀝니다.
allocArray에서 numArr1은 ptr 인수로 받게 됩니다.
따라서 ptr은 pointer to pointer to void이고,
*ptr은 pointer to void입니다.
함수 호출을 위해 임시로 void **로 변환한 것이므로 함수 호출이
끝난 다음에 numArr1은 원래의 long long* 타입이고,
numArr2는 원래의 int* 타입입니다.
포인터 단원은 책을 한 번 다 학습한 다음에
다시 반복학습하는 게 좋습니다.
답변 감사드립니다.
제가 이해가 안되면 그 뒤로 못넘어가는 성격이라.. 자꾸 앞에꺼랑 비교해서
이해가 안되면 머물게 되네요..
열공하겠습니다.
안녕하세요
마지막으로 63.10에 대하여 질문이 있습니다
63.3 parameter_double_pointer.c과
63.10 judge_parameter_double_pointer.c이 거의 동일한 예제인데
63.3에는 (void**) 이중포인터 임시 형변환이 안들어갔더라구요
그 이유를 알수있을까요?
당연하지만 63.3에 (void**)를 추가해도 결과가 잘 나오더라구요
자꾸 질문해서 죄송합니다
감사합니다.
63.10 judge_parameter_double_pointer.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
void allocArray(void** ptr, int num, int size)
{
*ptr = malloc(size * num);
}
int main()
{
long long* numArr1;
int* numArr2;
allocArray((void**)&numArr1, 10, sizeof(long long));
allocArray((void**)&numArr2, 3, sizeof(int));
scanf("%lld %d", &numArr1[9], &numArr2[2]);
printf("%lld %d\n", numArr1[9], numArr2[2]);
free(numArr2);
free(numArr1);
return 0;
}
63.3 parameter_double_pointer.c
#include <stdio.h>
#include <stdlib.h>
void allocMemory(void** ptr, int size)
{
*ptr = malloc(size);
}
int main()
{
long long* numPtr;
allocMemory(&numPtr, sizeof(long long));
*numPtr = 10;
printf("%lld\n", *numPtr);
free(numPtr);
return 0;
}
(void**)이 들어가야 맞습니다.
오탈자로 등록하고, 종이책에는 다음 쇄에 반영하겠습니다.
현재 (void**)이 없는 상태에서는 무시할 수는 있지만, 경고가 발생합니다.
main.c:14:17: warning: incompatible pointer types passing 'long long **' to parameter of type 'void **' [-Wincompatible-pointer-types] allocMemory(&numPtr, sizeof(long long)); ^~~~~~~ main.c:4:25: note: passing argument to parameter 'ptr' here void allocMemory(void **ptr, int size) // 반환값 없음, void 이중 포인터 매개변수 지정 ^ 1 warning generated.
예제에서 (void**)이 들어가는 게 맞습니다.