59.4 구조체 포인터로 포인터 연산하기
구조체 포인터도 포인터 연산을 할 수 있습니다. 다음과 같이 포인터 연산을 한 부분을 ( ) (괄호)로 묶어준 뒤 -> (화살표 연산자) 연산자를 사용하여 멤버에 접근하면 됩니다.
- (포인터 + 값)->멤버
- (포인터 - 값)->멤버
다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요.
struct_pointer_add.c
#include <stdio.h> struct Data { int num1; int num2; }; int main() { struct Data d[3] = { { 10, 20 }, { 30, 40 }, { 50, 60 } }; // 구조체 배열 선언과 값 초기화 struct Data *ptr; // 구조체 포인터 선언 ptr = d; // 구조체 배열 첫 번째 요소의 메모리 주소를 포인터에 저장 printf("%d %d\n", (ptr + 1)->num1, (ptr + 1)->num2); // 30 40: 구조체 배열에서 멤버의 값 출력 // d[1].num1, d[1].num2와 같음 printf("%d %d\n", (ptr + 2)->num1, (ptr + 2)->num2); // 50 60: 구조체 배열에서 멤버의 값 출력 // d[2].num1, d[2].num2와 같음 return 0; }
실행 결과
30 40 50 60
구조체 배열 d를 선언한 뒤 첫 번째 요소의 메모리 주소를 ptr에 저장했습니다. 이제 ptr로 구조체 배열에 접근할 수 있겠죠?
구조체 포인터는 (ptr + 1)->num1와 같이 포인터 연산을 한 뒤 괄호로 묶어줍니다. 그리고 화살표 연산자를 사용하여 멤버에 접근할 수 있습니다(괄호는 반드시 사용해야 합니다). 결과적으로 (ptr + 1)->num1은 구조체 배열의 인덱스로 접근하는 d[1].num1과 같습니다.
구조체 Data의 크기는 4바이트짜리 int형 멤버가 두 개 들어있으므로 8바이트입니다. 따라서 포인터 연산을 하면 8바이트씩 메모리 주소에서 더하거나 뺍니다. 만약 구조체가 커져서 int형 멤버가 10개가 된다면 40바이트씩 더하거나 빼게 됩니다.
![](https://dojang.io/pluginfile.php/576/mod_page/content/26/unit59-5.png)
이번에는 void 포인터에 구조체 3개 크기만큼 동적 메모리를 할당한 뒤 포인터 연산을 해보겠습니다.
- ((struct 구조체이름 *)포인터 + 값)->멤버
- ((struct 구조체이름 *)포인터 - 값)->멤버
다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요.
struct_void_pointer_add.c
#include <stdio.h> #include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일 #include <string.h> // memcpy 함수가 선언된 헤더 파일 struct Data { int num1; int num2; }; int main() { void *ptr = malloc(sizeof(struct Data) * 3); // 구조체 3개 크기만큼 동적 메모리 할당 struct Data d[3]; ((struct Data *)ptr)->num1 = 10; // 포인터 연산으로 메모리에 값 저장 ((struct Data *)ptr)->num2 = 20; // 포인터 연산으로 메모리에 값 저장 ((struct Data *)ptr + 1)->num1 = 30; // 포인터 연산으로 메모리에 값 저장 ((struct Data *)ptr + 1)->num2 = 40; // 포인터 연산으로 메모리에 값 저장 ((struct Data *)ptr + 2)->num1 = 50; // 포인터 연산으로 메모리에 값 저장 ((struct Data *)ptr + 2)->num2 = 60; // 포인터 연산으로 메모리에 값 저장 memcpy(d, ptr, sizeof(struct Data) * 3); // 동적 메모리가 구조체 배열의 형태와 같은지 // 확인하기 위해 동적 메모리의 내용을 구조체 배열에 복사 printf("%d %d\n", d[1].num1, d[1].num2); // 30 40: 구조체 배열의 멤버 출력 printf("%d %d\n", ((struct Data *)ptr + 2)->num1, ((struct Data *)ptr + 2)->num2); // 50 60: 포인터 연산으로 메모리의 값 출력 free(ptr); // 동적 메모리 해제 return 0; }
실행 결과
30 40 50 60
문법이 복잡해 보이지만 어렵지 않습니다. ((struct Data *)ptr)->num1은 앞에서 배운 구조체 포인터로 변환하는 방법입니다. 이 상태에서 포인터 연산을 하려면 ((struct Data *)ptr + 1)->num1와 같이 ptr을 구조체 포인터로 변환한 뒤 값을 더해주면 됩니다. ->(화살표 연산자) 연산자를 사용하려면 반드시 괄호로 묶어줍니다.
이제 포인터 연산을 통해 메모리에 값을 저장합니다. 만약 (ptr + 1)->num1처럼 ptr에 포인터 연산을 하더라도 ptr은 void 포인터라 Data 구조체의 형태를 모르기 때문에 멤버에 접근할 수 없고 컴파일 에러가 발생합니다.
((struct Data *)ptr + 1)->num1 = 30; // 포인터 연산으로 메모리에 값 저장 ((struct Data *)ptr + 1)->num2 = 40; // 포인터 연산으로 메모리에 값 저장 ((struct Data *)ptr + 2)->num1 = 50; // 포인터 연산으로 메모리에 값 저장 ((struct Data *)ptr + 2)->num2 = 60; // 포인터 연산으로 메모리에 값 저장
그리고 포인터 연산으로 값을 저장한 결과가 Data 구조체 배열의 형태와 같은지 확인하기 위해 memcpy(d, ptr, sizeof(struct Data) * 3);처럼 동적 메모리의 내용을 구조체 배열 d에 복사했습니다.
다음과 같이 printf로 구조체 배열 d의 멤버를 출력해보면 포인터 연산을 통해 저장한 값이 출력되는 것을 볼 수 있습니다. 즉, 동적 메모리에 저장된 값의 위치가 구조체 배열의 형태와 같고, 동적 메모리 내용을 그대로 복사했기 때문에 같은 값이 나옵니다. 또한, 포인터 연산으로도 동적 메모리의 값을 출력할 수 있습니다.
printf("%d %d\n", d[1].num1, d[1].num2); // 30 40: 구조체 배열의 멤버 출력 printf("%d %d\n", ((struct Data *)ptr + 2)->num1, ((struct Data *)ptr + 2)->num2); // 50 60: 포인터 연산으로 동적 메모리의 값 출력
![](https://dojang.io/pluginfile.php/576/mod_page/content/26/unit59-6.png)