Q & A

포인터 연산은 꼭 해야하나요?

보통 배열로는 처리하기가 까다로운 문제일 때 포인터 연산을 사용합니다(관련 예제는 'Unit 83 실전 예제: JSON 파일 읽고 쓰기' 참조). 특히 C 언어 표준 함수들은 메모리 주소를 요구하는 경우가 많기 때문에 포인터 연산이 자주 활용됩니다.

void 포인터는 포인터 연산을 할 수 없나요?

C 언어 표준에서 void 포인터는 포인터 연산을 할 수 없습니다. 일단 Visual Studio에서는 void 포인터 연산을 컴파일 에러로 처리하고 있지만 GCC에서는 void포인터를 char, unsigned char 크기만큼 연산합니다(char, unsigned char의 크기는 1바이트). 즉, 다음과 같이 void 포인터에 1을 더하면 메모리 주소가 1바이트 증가합니다.

void *ptr = malloc(100);

printf("%p\n", ptr);        // 0x181d010
printf("%p\n", ptr + 1);    // 0x181d011: void 포인터에 1을 더하면 메모리 주소가 1바이트 증가
                            // GCC에서 void 포인터는 char, unsigned char 크기만큼 연산
                            // Visual Studio에서는 컴파일 에러

free(ptr);

void 포인터 연산 기능은 GCC의 확장(extension)인데 C 언어 표준에 따라 문법을 검사하려면 명령줄 옵션에서 -ansi -pedantic를 지정합니다. 또는 -Wpointer-arith 옵션을 사용해도 됩니다

$ gcc main.c -ansi -pedantic
main.c: In function main:
main.c:9:24: warning: pointer of type void * used in arithmetic [-Wpedantic]
     printf("%p\n", ptr + 1);
                        ^

void 포인터 연산은 호환성을 위해 사용하지 않는 것이 좋습니다. 즉, 표준을 지켜야 호환성이 좋은 코드가 됩니다. 만약 메모리를 1바이트씩 접근하려면 unsigned char *를 사용하면 됩니다.

memset 함수로 메모리 중간부터 값을 설정할 수는 없나요?

포인터 연산을 활용하면 메모리 중간부터 값을 설정할 수 있습니다. 다음은 (unsigned char *)ptr + 10으로 메모리 주소를 10바이트 증가시킨 뒤 30바이트만큼 0으로 설정합니다.

void *ptr = malloc(100);    // 100바이트만큼 메모리 할당

memset((unsigned char *)ptr + 10, 0, 30);    // 10바이트 증가시킨 뒤  30바이트만큼 0으로 설정

free(ptr);

malloc 함수로 메모리를 할당할 때 왜 자료형 변환을 하지 않나요?

malloc 함수는 void 포인터를 반환하기 때문입니다. void 포인터는 자료형을 직접 변환하지 않아도 할당할 자료형으로 알아서 변환됩니다.

struct Data *d1 = malloc(sizeof(struct Data));    // malloc은 void *를 반환

컴파일 경고와 컴파일 에러의 차이는 무엇인가요?

컴파일 경고는 프로그램이 실행은 되지만 잠재적으로 문제가 될 수 있는 부분을 알려주는 역할을 합니다. 그러므로 컴파일 경고가 발생하는 부분은 문제가 없는지 살펴보고 경고가 발생하지 않도록 만들어주어야 합니다.

컴파일 에러는 문법이 잘못되어 컴파일 자체가 불가능한 경우입니다. 단, 컴파일러 설정에 따라서 컴파일 경고도 컴파일 에러로 처리할 수 있습니다.

포인터는 마이너스 연산을 할 수 있나요?

네. 포인터는 마이너스 연산을 할 수 있습니다. 포인터에서 값을 빼면 메모리 주소가 작아지므로 포인터가 역방향으로 이동(후진, backward)합니다.

int *numPtr;

// 생략 ...

numPtr = numPtr - 1;    // 4바이트만큼 역뱡향으로 이동