티스토리 뷰

728x90
반응형

 C에 입문하면 포인터라는 장벽이 있다. 거기에 2차원 포인터 배열 들어가면 머리 속이 뒤죽박죽이다. 그래서 나의 머리를 정리하게 위해 포인터 배열을 정리해보았다.

 포인터의 산술연산을 사용하는 경우 포인터가 배열인 것 처럼 사용할 수 있고 연산도 포인터와 배열이 비슷하다. 이 두가지의 공통점과 차이점을 살펴보자

 

1차원 배열과 포인터

우선 아래 예제를 살펴보자.

#include <stdio.h>

int main()
{
	int arr[10];

	int num = sizeof(arr) / sizeof(arr[0]);

	for (int i = 0; i < num; ++i)
		arr[i] = (i + 1) * 100;

	int* ptr = arr;
    
    printf("%p %p %p\n", ptr, arr, &arr[0]); // 0x7ffdad519460 0x7ffdad519460 0x7ffdad519460 

	ptr += 2;

	printf("%p %p %p\n", ptr, arr + 2, &arr[2]); // 0x7ffdad519468 0x7ffdad519468 0x7ffdad5194608

	// Note: arr += 2 // invalid // 배열의 이름이 포인터처럼 사용될 수 있지만 포인터와 완전히 동일하지 않는다.
	
	// ()를 사용하는 경우 포인터 주소값으로 계산하여 출력한다..
	printf("%d %d %d\n", *(ptr + 1), *(arr + 3), arr[3]); // 400 400 400
	
	// Warning
	// ()를 사용하지 않으면 *변수 계산 후 연산을 진행한다.
	printf("%d %d %d\n", *ptr + 1, *arr + 3, arr[3]); // 301 103 400

	for (int i = 0, *ptr = arr; i < num; ++i)
	{
		printf("%d %d %d\n", *ptr++, arr[i], *(ptr+i)); // 100 100 200 200 300 300 ... 1000 1000
		printf("%d %d\n",  *ptr + i); // 101 102 103 ... 
	}
    
    }

 

우선 arr 배열을 초기화 하자.

// arr 배열을 초기화
    int arr[10];

	int num = sizeof(arr) / sizeof(arr[0]);

	for (int i = 0; i < num; ++i)
		arr[i] = (i + 1) * 100;
   // arr[10] = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000}

초기화한 arr 배열을 ptr 포인터 변수에 대입한다.(포인터 변수 선언 int* ptr;)

여기서 arr 자체는 arr의 주소를 가리키기 때문에 ptr 포인터 변수에 대입이 된다.

int* ptr = arr;

그런 다음 포인터 배열의 주소와 값을 가져오는 코드를 분석해보자.

 

1차원 포인터 배열 주소를 조회하는 코드는 아래와 같다.

 printf("%p %p %p\n", ptr, arr, &arr[0]); // 0x7ffdad519460 0x7ffdad519460 0x7ffdad519460

 

역참조(dereferencing))

포인터의 값을 조회하기 위해 역참조를 해야한다. 그래서 * 연산자를 사용해 역참조를 한다.

1차원 포인터 배열 값를 조회하는 코드는 아래와 같다.

ptr+=2;
printf("%d %d %d\n", *(ptr + 1), *(arr + 3), arr[3]); // 400 400 400

포인터 주소에서 값을 가져오려면(역참조(dereferencing)) *을 사용하야 한다.

ptr +=2를 해주면 ptr 포인터 변수가 참조하고 있는 주소는 포인터 주소 크기의 2칸만큼 이동하고 *(ptr+1)까지 해주면 포인터 주소 크기의 3칸을 이동하게 되어 *(ptr + 1) == *(arr + 3) == arr[3] 된다.

 

포인터 배열 값을 조회할 때 주의해야할 점은

 *ptr + 1 과 *(ptr+1) 은 다르다는 것이다.  *ptr + 1는 포인터 주소에서 역참조한 값에 +1을 하는 것이고 (*ptr+1)은 포인터 배열의 주소 크기만큼 한 칸 이동하여 값을 역참조하여 가져온다는 점이다.

	// ()를 사용하는 경우 포인터 주소값으로 계산하여 출력한다..
	printf("%d %d %d\n", *(ptr + 1), *(arr + 3), arr[3]); // 400 400 400
	
	// ()를 사용하지 않으면 *변수 계산 후 연산을 진행한다.
	printf("%d %d %d\n", *ptr + 1, *arr + 3, arr[3]); // 301 103 400

 

주의!! 여기서 배열 이름 자체만 가지고 연산은 안된다.

arr += 2  //Error

 

1차원 배열까지는 괜찮은데 2차원 배열이 나오면 머리가 아프다. 이해해야될 부분도 있고 외워야할 부분이 있다.

 

2차원 배열과 포인터

#include <stdio.h>

int main()
{
    int arr[2][3] = {{1,5,6}, {7,3,8}};
    
    printf("arr[0][0] 주소 가져오기\n");
    printf("arr address is %p\n", arr);     // arr address is 0x7fff789b4d80
    printf("arr address is %p\n", *arr);    // arr address is 0x7fff789b4d80
    printf("arr address is %p\n", arr[0]);  // arr address is 0x7fff789b4d80
    printf("arr address is %p\n", &arr[0]); // arr address is 0x7fff789b4d80
    printf("arr address is %p\n", &arr[0][0]);// arr address is 0x7fff789b4d80
    
    printf("arr[0][0] 값 가져오기\n");
    printf("arr value is %d\n", **arr); // arr value is 1
    printf("arr value is %d\n", arr[0][0]); // arr value is 1

    printf("arr[1][0] 주소 가져오기\n");
    printf("arr address is %p\n", arr+1);   // arr is 2 0x7fff789b4d8c
    printf("arr address is %p\n", &arr[1]); // arr is 2 0x7fff789b4d8c
    printf("arr address is %p\n", arr[1]);  // arr is 2 0x7fff789b4d8c
    printf("arr address is %p\n", &arr[0]+1);// arr is 2 0x7fff789b4d8c
    printf("arr address is %p\n", &arr[1][0]); // arr is 2 0x7fff789b4d8c
    
    printf("arr[1][0] 값 가져오기\n");
    printf("arr value is %d\n", *(*(arr+1))); // arr value is 7
    printf("arr value is %d\n", arr[1][0]);   // arr value is 7
    
    printf("arr[1][2] 값 가져오기\n");
    printf("arr value is %d\n", arr[1][2]);     // arr value is 8
    printf("arr value is %d\n", *(*(arr+1)+2)); // arr value is 8
    
    printf("arr[0][0] +1 값 가져오기\n");
    printf("arr value is 2 %d\n", **arr+1); // arr value is 2
    }

 

위 예제에서

arr == *arr == arr[0] == &arr[0] == &arr[0][0] 이다

특이한 점은 arr[0]이 arr와 같으니 &arr[0] 은 arr의 주소로 헷갈릴 수 있는데 C 언어에서 arr == &arr[0] 로 작동한다.

 

2차원 배열 포인터 주소 변수에서 역참조하여 값을 가져오는 방법은 이중 포인터를 사용할 수 있다.

**arr == arr[0][0] 이다. 

1차원 배열 포인터 주소 변수에서는 *arr = arr[0]이고 2차원 배열에서는 **arr == arr[0][0]이다.

즉, 이중포인터를 사용하여 2차원 배열처럼 활용할 수 있다.

만약 arr[1][2]를 가져오고 싶다면 *(*(arr + 1) + 2)를 사용하면 된다.

arr[i][j] == *(*(arr + i) + j) 처럼 사용해야한다.

 

여기서 주의해야할 점은 **arr + 1은 arr[0][0] + 1 로 1+1 = 2가 되는 점이다.

 

1차원 포인터 배열과 2차원 포인터 배열를 살펴보았다.

외워야할 부분도 있고 이해해야하는 부분도 있다.

잘  정리해서 C언어를 잘 사용해보자.

728x90
반응형
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/09   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함