Coding/C

[C언어] 배열, 배열 포인터 사용하기 - 코딩도장

GunP4ng 2024. 1. 7. 23:56

[코딩도장] Unit 36 ~ 38


1. 배열

1. 배열 사용하기

C언어에서 배열은 변수 이름뒤에 [ ] 대괄호를 붙여서 선언한다

배열의 값을 초기화 할 때는 { } 중괄호를 사용한다

{ } 는 배열을 선언할 때만 사용한다

int numArr[10] = { 11, 22, 33, 44, 55, 66, 77, 88, 99, 110 };

 

int numArr[10]은 크기가 10인 int 형 배열을 선언한다는 뜻이다

{ } 안의 값 개수는 배열의 크기보다 크면 안된다

 

배열의 요소에 접근하려면 배열 뒤의 [ ] 안에 각 요소의 인덱스를 지정해주면 된다

인덱스로 배열의 요소에 접근

※ 배열의 인덱스는 항상 0부터 시작한다

numArr 배열의 첫번째 요소는 numArr[0] 이 된다

 

배열의 크기는 sizeof 연산자를 이용해서 구한다

int numArr[10] = { 11, 22, 33, 44, 55, 66, 77, 88, 99, 110 };

printf("%d\n", sizeof(numArr));                  // 40
printf("%d\n", sizeof(numArr) / sizeof(int));    // 10

sizeof(numArr) 은 4바이트 요소가 10개 이므로 40이 된다

배열의 크기는 전체 공간 / 요소의 크기 를 하면 된다

 

 

2. 배열 포인터

배열은 주소값이기 때문에 포인터에 넣을 수 있다

int numArr[10] = { 11, 22, 33, 44, 55, 66, 77, 88, 99, 110 };

int *numPtr = numArr;

배열을 포인터에 바로 할당하려면

자료형이 같아야 하고 1차원 배열이라면 * 가 한 개인 단일 포인터여야 한다

 

printf("%d\n", *numPtr);    // 11

printf("%d\n", *numArr);    // 11

배열을 포인터에 할당한 뒤 역참조 해보면 배열의 첫번째 요소의 값이 나온다

배열 자체도 역참조 해보면 배열의 첫번째 요소의 값이 나온다

실제로는 배열도 포인터이다

 

배열과 포인터가 다른점은 sizeof 로 크기를 계산할 때이다

printf("%d\n", sizeof(numArr));    // 40

printf("%d\n", sizeof(numPtr));    // 32비트라면 4, 64비트라면 8

배열에 sizeof 연산자를 사용하면

배열이 차지하는 전체 공간의 크기가 나온다

 

배열의 주소가 들어있는 포인터의 크기를 구하면

그냥 포인터의 크기만 나온다


2. 2차원 배열

1. 2차원 배열 사용하기

2차원 배열은 [ ] 를 두 번 사용하여 선언한다

int numArr[3][4] = {
    { 가로 요소 4개 },
    { 가로 요소 4개 },
    { 가로 요소 4개 }
}; //       ↑ 세로 3줄

{ } 안의 값과 줄 개수는 가로, 세로 크기보다 작아도 되지만 크면 안된다

 

배열의 요소에 접근하려면

int numArr[3][4] = {    // 세로 크기 3, 가로 크기 4인 int형 2차원 배열 선언
        { 11, 22, 33, 44 },
        { 55, 66, 77, 88 },
        { 99, 110, 121, 132 }
    };

int num1 = numArr[1][2];    // 77

[ ] 를 두 번 사용하여 세로와 가로 인덱스를 지정해주면 된다

 

인덱스로 2차원 배열의 요소에 접근

2차원 배열도 인덱스는 0부터 시작한다

numArr 배열의 가로 첫번째, 세로 첫번째 요소는 numArr[0][0] 이다

 

2차원 배열의 크기도 sizeof 로 구할 수 있다

printf("%d\n", sizeof(numArr));    // 48

배열의 크기를 구하면 배열이 차지하는 전체 공간이 출력된다

4바이트 요소가 12개(4 * 3) 이므로 48

 

int col = sizeof(numArr[0]) / sizeof(int);

가로( column, 열) 의 요소 개수는 가로 한 줄의 크기 / 요소의 크기 로 구한다

 

int row = sizeof(numArr) / sizeof(numArr[0]);

세로(row, 행) 의 요소 개수는 배열의 전체 공간 / 가로 한줄 의 크기 로 구한다

 

 

2. 2차원 배열 포인터

2차원 배열은 아래와 같이 포인터에 담을 수 있다 

int (*numPtr)[4];

포인터를 선언할 때 * 과 포인터 이름을 ( ) 로 묶고 크기를 지정해야 한다

가로 크기가 4인 배열을 가리키는 포인터라는 의미이다

2차원 배열과 포인터

int (*numPtr)[4] = numArr;

2차원 배열을 포인터에 바로 할당할 수도 있다

하지만 자료형과 포인터의 가로 크기가 같아야 한다


3. 포인터와 배열 응용하기

1. 포인터를 배열처럼 사용하기

포인터에 malloc 함수로 메모리를 할당해주면 배열처럼 사용할 수 있다

int *numPtr = malloc(sizeof(int) * 10);

free(numPtr);

int 크기에 10을 곱하여 동적으로 메모리를 할당한다

 

배열은 한 번 선언하면 끝이지만

malloc 으로 메모리를 할당한 포인터는 free 함수로 해제 해주어야 한다

포인터를 배열처럼 사용

 

 

2. 포인터를 2차원 배열처럼 사용하기

int **m = malloc(sizeof(int *) * 3);

이중 포인터에 2차원 배열의 세로 공간에 해당하는 메모리를 할당한다

세로 공간에는 값이 들어가지 않고 가로 공간의 메모리 주소가 들어간다

포인터의 크기( int * ) * 3 을 한다

이중 포인터에 배열의 세로 공간 할당

 

for (int i = 0; i < 3; i++)            // 세로 크기만큼 반복
{
    m[i] = malloc(sizeof(int) * 4);    // (int 크기 * 가로 크기)만큼 동적 메모리 할당.
                                       // 배열의 가로
}

세로 크기만큼 반복하면서 2차원 배열의 가로 공간의 메모리를 할당한다

가로에는 int 형 숫자가 들어가므로 sizeof( int ) * 4 를 한다

이중 포인터에 배열의 가로 공간 할당

 

포인터를 다 사용했다면

for (int i = 0; i < 3; i++)    // 세로 크기만큼 반복
{
    free(m[i]);                // 2차원 배열의 가로 공간 메모리 해제
}

free(m);

가로 공간에 해당하는 메모리 먼저 해제한다

그 다음 세로 공간에 해당하는 메모리를 해제한다

세로 → 가로 로 할당했기 때문에 반대로 가로 → 세로 로 해제한다

※ 세로 공간에 해당하는 메모리를 먼저 해제하면 가로 공간 메모리를 해제할 수 없다


4. 심사문제

36.13 가장 작은 수 출력하기

표준 입력으로 정수 다섯 개가 입력됩니다(입력 값의 범위는 -2147483648~2147483647). 다음 소스 코드를 완성하여 입력된 정수 중에서 가장 작은 수가 출력되게 만드세요.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int numArr[5];
    int smallestNumber;

    scanf("%d %d %d %d %d", &numArr[0], &numArr[1], &numArr[2], &numArr[3], &numArr[4]);

    _____________________________________
    _____________________________________
    _____________________________________
    _____________________________________
    _____________________________________
    _____________________________________ 
    _____________________________________
    _____________________________________

    printf("%d\n", smallestNumber);

    return 0;
}

 

정답

smallestNumber = numArr[0];

for (int i = 0; i < 5; i++) {
    if (smallestNumber > numArr[i]) {
        smallestNumber = numArr[i];
    }
}

smallestNumber 에 배열의 첫번째 요소를 넣고

배열의 값과 비교하면서 배열의 값이 더 작으면

smallestNumber 에 값을 저장한다


37.9 전치행렬 구하기

표준 입력으로 5x5 정사각행렬이 입력됩니다. 다음 소스 코드를 완성하여 입력된 행렬의 전치행렬이 출력되게 만드세요.

전치행렬은 왼쪽 위부터 오른쪽 아래까지의 대각선(주대각선)을 기준으로 값을 뒤집은 행렬을 말합니다.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    int matrix[5][5];

    scanf("%d %d %d %d %d", 
        &matrix[0][0], &matrix[0][1], &matrix[0][2], &matrix[0][3], &matrix[0][4]);
    scanf("%d %d %d %d %d", 
        &matrix[1][0], &matrix[1][1], &matrix[1][2], &matrix[1][3], &matrix[1][4]);
    scanf("%d %d %d %d %d", 
        &matrix[2][0], &matrix[2][1], &matrix[2][2], &matrix[2][3], &matrix[2][4]);
    scanf("%d %d %d %d %d", 
        &matrix[3][0], &matrix[3][1], &matrix[3][2], &matrix[3][3], &matrix[3][4]);
    scanf("%d %d %d %d %d", 
        &matrix[4][0], &matrix[4][1], &matrix[4][2], &matrix[4][3], &matrix[4][4]);

    ______________________________________
    ______________________________________
    ______________________________________
    ______________________________________
    ______________________________________
    ______________________________________
    ______________________________________
    ______________________________________

    return 0;
}

 

정답

int row = sizeof(matrix) / sizeof(matrix[0]);
int col = sizeof(matrix[0]) / sizeof(int);

for (int i = 0; i < row; i++) {
    for (int j = 0; j < col; j++) {
        printf("%d ", matrix[j][i]);
    }
    printf("\n");
}

배열의 전체 크기 / 배열의 가로 크기 로 행의 크기를 구한다

배열의 가로 크기 / 요소의 크기 로 열의 크기를 구한다

 

전치 행렬은 행과 열을 교환하여 얻는 행렬이므로

matrix[j][i] 를 하여 전치 행렬을 출력한다


38.7 단위행렬 만들기

표준 입력으로 정사각행렬의 크기가 입력됩니다(입력 값의 범위는 2~10). 입력된 크기만큼의 단위행렬을 출력하는 프로그램을 만드세요(scanf 함수 호출 전에 문자열을 출력하면 안 됩니다). 숫자와 숫자 사이는 공백으로 띄웁니다.

단위행렬은 왼쪽 위부터 오른쪽 아래까지의 대각선(주대각선)이 1이며 나머지는 모두 0인 행렬입니다.

정답에는 C 언어 컴파일러에서 정상적으로 컴파일되는 전체 코드를 입력해야 합니다.

 

정답

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

int main() {
    int col, row, size;
    scanf("%d", &size);

    int **m = malloc(sizeof(int *) * size);     // 배열의 세로

    for (int i = 0; i < size; i++) {            // 배열의 가로
        m[i] = malloc(sizeof(int) * size);      
    }

    for (int i = 0; i < size; i++) {            // 주대각선 만들기
        for (int j = 0; j < size; j++) {
            if (i == j) 
                m[i][j] = 1;
            else
                m[i][j] = 0;
            printf("%d ", m[i][j]);
        }
        printf("\n");
    }

    for (int i = 0; i < size; i++) {            // 메모리 해제
        free(m[i]);
    }
    free(m);

    return 0;
}

size 를 scanf 로 입력 받고

배열의 세로와 가로 크기를 동일하게 size 로 설정한다

 

단위행렬의 주대각선은 배열의 세로 인덱스와 가로 인덱스가 같을 때

1을 출력하여 만들 수 있다

 

같지 않을 때는 0을 출력하여 단위행렬을 만든다


38.8 지뢰찾기

표준 입력으로 행렬의 크기 m, n과 문자(char) 행렬이 입력됩니다(m과 n의 범위는 3~10). 입력된 m, n은 공백으로 구분되며 행렬 안에서 *은 지뢰이고 .은 지뢰가 아닙니다. 지뢰가 아닌 요소에는 인접한 지뢰의 개수를 출력하는 프로그램을 만드세요(scanf 함수 호출 전에 문자열을 출력하면 안 됩니다).

 

정답

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

int main() {
    int col, row;
    char c;
    scanf("%d %d", &col, &row);

    char **m = malloc(sizeof(char *) * row);        // 배열의 세로 (메모리 할당)

    for (int i = 0; i < row; i++) {                 // 배열의 가로 (메모리 할당)
        m[i] = malloc(sizeof(char) * col);
    }

    // 배열 입력
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            scanf(" %c", &c);
            m[i][j] = c;
        }
    }

    // 현재 위치 확인하고 di, dj 에 저장
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            if (m[i][j] == '.') {
                int di = i;
                int dj = j;
                int count = 0;

                // 8 방향으로 지뢰가 있는지 확인
                for (int k = di - 1; k <= di + 1; k++) {
                    for(int l = dj - 1; l <= dj + 1; l++) {

                        // 인덱스가 범위 안에 있는지 확인
                        if ((k >= 0 && k < row) && (l >= 0 && l < col)) {
                            if (m[k][l] == '*') {
                            count += 1;
                            }
                        }
                    }
                }
                // 지뢰의 개수를 저장
                m[i][j] = count;
            }
        }
    }

    // 배열 출력
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            
            // * 는 문자이기 때문에 %c 사용
            if (m[i][j] == '*') {
                printf("%c", m[i][j]);
            }

            // count 는 int 로 저장했기 때문에 %d 사용
            else {
                printf("%d", m[i][j]);
            }
        }
        printf("\n");
    }

    // 가로 공간 메모리 해제
    for (int i = 0; i < row; i++) {
        free(m[i]);
    }

    //세로 공간 메모리 해제
    free(m);

    return 0;
}

배열을 %c 형식지정자를 이용해 문자를 하나씩 입력받았다

 

인접한 지뢰의 개수를 출력하기 위해서는

1. 현재 위치를 저장해야 한다

2. 현재 위치를 기준으로 8 방향을 확인한다

3. * 이 있다면 count + 1 을 한다

4. count 의 값을 현재 위치에 저장한다

 

3 x 3  배열을 아래처럼 입력한다고 했을 때

...    // 00 01 02
.*.    // 10 11 12
...    // 20 21 22

 

[1][1] 을 di, dj 에 저장하면

int di = 1;
int dj = 1;

8 방향의 범위는

di - 1 ~ di + 1

dj - 1 ~ dj + 1 이 된다

 

따라서

// 현재 위치 확인하고 di, dj 에 저장
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            if (m[i][j] == '.') {
                int di = i;
                int dj = j;
                int count = 0;

배열의 요소가 . 이라면

현재 위치를 di , dj 에 저장하고

count (지뢰) 를 0으로 초기화 한다

 

// 8 방향으로 지뢰가 있는지 확인
for (int k = di - 1; k <= di + 1; k++) {
    for(int l = dj - 1; l <= dj + 1; l++) {

        // 인덱스가 범위 안에 있는지 확인
        if ((k >= 0 && k < row) && (l >= 0 && l < col)) {
            if (m[k][l] == '*') {
                count += 1;
            }
        }
    }
}

8 방향의 위치를 확인하기 위해

di - 1 ~ di + 1

dj - 1 ~ dj + 1

의 범위를 반복하고

 

i , j 가 0일 때를 생각해서

di - 1 ~ di + 1

dj - 1 ~ dj + 1

의 범위가 0보다 크고 row, col 보다 작은지 확인한다

범위가 맞고 * 이 있다면

count + 1 을 한다

 

배열을 다시 출력하려면

// 배열 출력
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            
            // * 는 문자이기 때문에 %c 사용
            if (m[i][j] == '*') {
                printf("%c", m[i][j]);
            }

            // count 는 int 로 저장했기 때문에 %d 사용
            else {
                printf("%d", m[i][j]);
            }
        }
        printf("\n");
    }

지뢰가 있던 곳은 바뀌지 않았기 때문에

%c 형식지정자를 이용해서 문자로 출력을 해준다

 

* 이 아니라면 지뢰의 개수를 int 형으로 저장했기 때문에

%d 형식지정자를 이용해서 숫자로 출력해준다

 

마지막으로 메모리를 가로 세로 순으로 해제해주면 된다