[C] 다차원 포인터와 활용

2021. 1. 22. 18:52프로그래밍 공부/C 공부

포인터 복습

포인터는 가리키는 대상의 메모리 주소를 저장하여 사용할 수 있도록 하는것이다.

void main()
{
int *p;
int data = 3;
p = &data;

printf("Before data : %d", data)//결과값 3
*p = 5;
printf("Use *p data : %d", *data); //결과값 5

}

 

다차원 포인터

위의 포인터 *p는 1차원 포인터인데, 포인터가 다른 포인터를 가리키도록 만듦으로써 2차원, 3차원...n차원 포인터를 만들 수 있다. 

 

아래는 2차원 포인터 에시다.

int main()
{
  int **pp;
   int *p;
   int data = 3;
 
   pp=&p;
   p = &data;
 
   printf("Before data : %d\n", data); //결과값 3
  *p=4;
   printf("Use *p, data : %d\n", data); //결과값 4
   **pp=5;
   printf("Use **p, data : %d\n", data); //결과값 5

}

2차원 포인터는 *를 두개 사용함으로써 만들 수 있고, 최종값(**p)에 도달하기 직전 단계에 *p를 거치도록 하였다. 

**p에 값을 입력하면, &p를 거쳐, p가 가리키고있는 &data에 도달하는 것이다. 

 

위의 예시에서는 포인터에 정적할당을 이용하였다.

동적 할당으로 포인터에 메모리 공간을 할당하려면 어떻게 해야할까?

#include <stdio.h>
#include <malloc.h>

int main()
{
    int **pp;
    pp = (int **)malloc(sizeof(int));
    *pp = (int *)malloc(sizeof(int));
    
    **pp = 3;
 
 
 printf("Before data : %d\n", **pp); //결과값 3

}

[ 포인터 변수 = 주소 ] : 포인터 변수가 가리키는 메모리 공간 

이 규칙만 지켜지면 동적할당으로도 충분히 포인터에 메모리 공간을 할당할 수 있다. 

 

 

활용1. 함수를 통한 포인터 동적할당

그러면 굳이 왜 다차원 포인터가 필요한걸까?

main에 선언된 포인터에 공간을 할당하는 함수를 만들때 다차원 포인터가 유용하게 사용될 수 있다. 

//실행 오류//
#include <stdio.h>
#include <malloc.h>

int assignM(int *q);

int main()
{
    int *p;
    assignM(p);
    *p = 5;
    free(p);

}

int assignM(int *q)
{
    q = (int *)malloc(8);
}

이 코드의 목적은 main에 선언된 포인터 p에 8바이트의 메모리 공간을 할당하는 것이지만, 뜻하는 결과가 나오지 않는다. 

 

main에서 assignM을 호출했을때 매개변수는 int *q = p, 즉 포인터 q에 p값을 입력하고, 함수에서 q가 가리키는 메모리를 8바이트로 할당했을 뿐, p에 어떠한 변화도 주지 못한것이다. 

 

그러면 마지막에 *p = q 를 입력하면 해결될까? 그렇지 않다. *p는 assignM 함수 외에 선언된 변수이기 때문에 assignM에서 활용할 수 없다.

 

이때 활용할 수 있는게 다차원 포인터이다. 

p에 메모리를 할당하는게 목적이라면 p를 가리키는 포인터가 있으면 되지 않을까?

//정상 실행//
#include <stdio.h>
#include <malloc.h>

int assignM(int **q);

int main()
{
    int *p;
    assignM(&p);
    *p = 5;
    free(p);

}

int assignM(int **q)
{
    *q = (int *)malloc(8);
}

assignM 함수가 호출되면, 매개변수는 int **q = &p  를 의미하고

함수 내부에서는 *q 가 가리키는 변수(**q 즉, &p) 에 8바이트의 메모리를 할당한다.

이러한 방법으로 포인터에동적 메모리 공간을 할당하는 함수를 만들때 다차원 포인터가 활용될 수 있다. 

 

 

활용2. 다차원 배열

20대~40대 직원들의 윗몸일이키기 점수를 입력하는 프로그램일 경우를 생각해보자.

20대 4명, 30대 2명, 40대 3명으로 이루어진 조직은 static하게 값을 고정하여 코딩할 수 있을 것이다.

#include <stdio.h>

int main()
{
    unsigned char limit_table[3] = { 4, 2, 3 }; //각연령대별 인원
    unsigned char count[3][4] = { 0, }; // 연령대3개 차원 * 최대인원 4명 메모리배정
    int age, member, temp, sum;

    for (age = 0; age < 3; age++)
    {
        printf("\n%d0대 연령의 윗몸 일으키기 횟수\n", age + 2);
        for (member = 0; member < limit_table[age]; member++)
        {
            printf("%dth:", member + 1);
            scanf("%d", &temp);
            count[age][member] = (unsigned char)temp;
        }
    }
 
    printf("\n\n연령별 평균 윗몸 일으키기 횟수\n");
    for (age = 0; age < 3; age++)
    {
        sum = 0;
        printf("%d0대 : ", age + 2);
        for (member = 0; member < limit_table[age]; member++)
        {
            sum = sum + count[age][member];
        }
        printf("%5.2f\n", (double)sum / limit_table[age]);
    }
}

하지만 각 연령별로 인원이 바뀐다면?

limit_table의 값을 반복문과 scanf로 받는다고쳐도 count의 두번째 차원의 값을 어떻게 유연하게 변화시킬 수 있을까?

 

포인터와 배열을 이용하면 해결할 수 있다. 

#include <stdio.h>
#include <malloc.h>

int main()
{
    unsigned char limit_table[3] = { 0, }; //연령병 직원수를 0으로 초기화
    unsigned char* p[3]; //3개의 요소를 갖는 포인터 선언
    int age, member, temp, sum;

    for (age = 0; age < 3; age++) {
        printf("%d0대 직원수를 입력하세요.", age + 2);
        scanf("%d", &temp); //scanf("%d", &limit_table); 는 오류가 발생해서 중간 과정 도입.
        limit_table[age] = (unsigned char)temp;
        *p[age] = (unsigned char *)malloc(limit_table[agㅂ2e]);

        printf("\n%d0대 연령의 윗몸 일으키기 횟수\n", age + 2);
        for (member = 0; member < limit_table[age]; member++)
        {
            printf("%dth:", member + 1);
            scanf("%d", &temp);
            *(p[age] + member) = (unsigned char)temp;
        }
    }

    printf("\n\n연령별 평균 윗몸 일으키기 횟수\n");
    for (age = 0; age < 3; age++)
    {
        sum = 0;
        printf("%d0대 : ", age + 2);
        for (member = 0; member < limit_table[age]; member++)
        {
            sum = sum + *(p[age] + member);
        }
        printf("%5.2f\n", (double)sum / limit_table[age]);
        free(p[age]);
    }

}

그렇다면 연령대 자체가 다양해진다면 어떻게 해야할까?

 

연령대를 입력받기 위한 값과, 각 연령대별 인원수를 입력받기 위한 값이 따로 주어져야할 것이다.

그리고 static하지 않고 가변적이어야 하므로 동적 할당을 이용해야한다.

 

#include <stdio.h>
#include <malloc.h>

void main()
{
    /* 연령별 인원수를 저장할 포인터 - 사용자에게 입력 받음 */
    unsigned char *p_limit_table;
    /* 연령별 윗몸 일으키기 횟수를 저장할 2차원 포인터 */
    unsigned char **p;
    int age, age_step, member, temp, sum;

    printf("20대부터 시작해서 연령층이 몇개인가요 : ");
    scanf_s("%d", &age_step);
   
    p_limit_table = (unsigned char*)malloc(age_step); 
    /* p가 가리키는 1차원 포인터를 age_step만큼 만듦.*/
    p = (unsigned char**)malloc(sizeof(unsigned char*) * age_step);
    
    
    /* 연령별로 윗몸 일으키기 횟수를 입력 받는다 */
    for (age = 0; age < age_step; age++) {
       
        printf("\n%d0대 연령의 윗몸 일으키기 횟수\n", age + 2);
        printf("이 연령대는 몇 명입니까? : ");
        /* 해당 연령에 소속된 사람수를 입력 받는다.*/
        scanf_s("%d", &temp);

        /* 입력 받은 인원수 만큼 메모리를 할당 한다
         p_limit_table + age 는 1바이트씩 메모리를 이동해서 값을 참조한다고 이해하면됨.
         char형태로 값을 받기 때문에 1바이트당 각 연령별 인원수가 저장되는 것과 같음.*/
        *(p_limit_table + age) = (unsigned char)temp;
        
        /* 위에서 age_step만큼 1차원 포인터를 선언했음. 
        각 1차원 포인터가 가리키는 최종값에 p_limit_table이 age영역에 참조한 정수를 입력한다. */
        *(p + age) = (unsigned char*)malloc(*(p_limit_table + age));
        
        /* 해당 연령에 소속된 사람들을 순서대로 입력 받는다. */
        for (member = 0; member < *(p_limit_table + age); member++) {
            printf("%dth : ", member + 1);
            scanf_s("%d", &temp);

            /* 2차원 포인터의 최종값에 적용 */
            *(*(p + age) + member) = (unsigned char)temp;
        }
    }

    printf("\n\n연령별 평균 윗몸 일으키기 횟수\n");

    for (age = 0; age < age_step; age++) {
        sum = 0;
        printf("%d0대 : ", age + 2);
        for (member = 0; member < *(p_limit_table + age); member++) {
            sum = sum + *(*(p + age) + member);
        }
        printf("%5.2f\n", (double)sum / *(p_limit_table + age));
        /* 이 연령에 할당했던 동적 메모리를 해제한다. */
        free(*(p + age));
    }
    /* 윗몸 일으키기 횟수를 저장하기 위해서 구성했던 메모리를 해제한다. */
    free(p);
    /* 연령별 인원수를 기억하기 위해서 사용했던 메모리를 해제한다. */
    free(p_limit_table);
}