31. 포인터와 배열
1. 키워드
- 가변 길이 배열(VLA: Variable-Length Array)
2. 포인터와 배열 응용하기
- 이번에는 포인터에 메모리를 할당하여 배열처럼 사용해 보자.
- 먼저 배열은 소스 코드에서 크기를 지정해서 생성해야 한다.
- 하지만 다음과 같이 프로그램 실행 중에 원하는 크기만큼 배열을 생성하는 기능(가변 길이 배열)은 GCC에서는 지원하지만, Visual Studio 2015에서는 지원하지 않는다.
#define _CRT_SECURE_NO_WARNING
#include <stdio.h>
int main()
{
int size;
scanf("%d", &size); // 배열의 크기를 입력받음
int numArr[size]; // GCC에서는 사용 가능, Visual Studio 2015에서는 컴파일 에러
return 0;
}
- 크기를 동적으로 지정하려면 포인터를 선언하고 메모리를 할당한 뒤 메모리를 배열처럼 사용해야 한다.
가변 길이 배열
- C에서는
int numArr[10];
처럼 고정된 크기로 배열을 생성하는 것은 가능하지만int numArr[size];
처럼 크기를 동적으로 지정할 수는 없다. - 이후 C99 표준이 제정되었고 가변 길이 배열 기능도 추가되었다.
- GCC, Clang 등의 컴파일러는 가변 길이 배열을 지원하지만 Visual Studio 2015는 지원하지 않는다.
3. 포인터에 할당된 메모리를 배열처럼 사용하기
- 포인터를 배열처럼 사용하는 방법은 포인터에
malloc
함수로 메모리를 할당해 주면 된다.
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일
int main()
{
int *numPtr = malloc(sizeof(int) * 10); // int 타입 10개 크기만큼 동적 메모리 할당
numPtr[0] = 10; // 배열처럼 인덱스로 접근하여 값 할당
numPtr[9] = 20; // 배열처럼 인덱스로 접근하여 값 할당
printf("%d\n", numPtr[0]); // 배열처럼 인덱스로 접근하여 값 출력
printf("%d\n", numPtr[9]); // 배열처럼 인덱스로 접근하여 값 출력
free(numPtr); // 동적으로 할당한 메모리 해제
return 0;
}
// 10
// 20
int *numPtr = malloc(sizeof(int) * 10);
과 같이int
타입 크기에10
을 곱하여 동적으로 메모리를 할당한다.- 그리고 배열처럼
[]
안에 인덱스를 지정하여 값을 할당하거나 가져올 수 있다. - 즉, 배열과 메모리가 할당된 포인터는 생성 방법만 다를 뿐 값을 다루는 방법은 같다.
numPtr[0] = 10; // 배열처럼 인덱스로 접근하여 값 할당
numPtr[9] = 20; // 배열처럼 인덱스로 접근하여 값 할당
printf("%d\n", numPtr[0]); // 배열처럼 인덱스로 접근하여 값 출력
printf("%d\n", numPtr[9]); // 배열처럼 인덱스로 접근하여 값 출력
free(numPtr); // 동적으로 할당한 메모리 해제
- 단, 배열
numArr
은 한 번 선언하면 끝이지만 포인터numPtr
은malloc
함수로 메모리를 할당했기 때문에free
함수로 해제해 준다. *numPtr
처럼 포인터를 역참조한 것과numPtr[0]
인덱스0
에 접근한 것은 같은 값을 가져온다.- 그리고
numPtr[1]
과*(numPtr + 1)
도 같은 값을 가져오는데,*(numPtr + 1)
과 같이 포인터에 값을 더하는 방식을 포인터 연산이라고 한다.
4. 입력한 크기만큼 메모리를 할당하여 배열처럼 사용하기
- 이번에는 사용자가 입력한 크기만큼 메모리를 할당하여 배열처럼 사용해 보자.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일
int main()
{
int size;
scanf("%d", &size);
int *numPtr = malloc(sizeof(int) * size); // (int 타입 크기 * 입력받은 크기)만큼 동적 메모리 할당
for (int i = 0; i < size; i++) // 입력받은 크기만큼 반복
{
numPtr[i] = i; // 인덱스로 접근하여 값 할당
}
for (int i = 0; i < size; i++) // 입력받은 크기만큼 반복
{
printf("%d\n", numPtr[i]); // 인덱스로 접근하여 값 출력
}
free(numPtr); // 동적으로 할당한 메모리 해제
return 0;
}
// 5 (입력)
// 0
// 1
// 2
// 3
// 4
// ↑ 5를 입력했으므로 0부터 4까지 값이 들어갔음
scanf
로 크기를 입력받았다.- 그리고
int
타입 크기에 입력받은 크기를 곱하여 메모리를 할당한다.
- 메모리가 준비되었으면 입력받은 크기만큼 반복하면서 값을 할당하고, 다시 입력받은 크기만큼 반복하면서 값을 출력한다.
- 배열과 마찬가지로 값을 할당하거나 출력할 때는
[]
를 사용하여 인덱스로 접근하면 된다.
for (int i = 0; i < size; i++) // 입력받은 크기만큼 반복
{
numPtr[i] = i; // 인덱스로 접근하여 값 할당
}
for (int i = 0; i < size; i++) // 입력받은 크기만큼 반복
{
printf("%d\n", numPtr[i]); // 인덱스로 접근하여 값 출력
}
- 사용이 끝났으면 반드시
free
함수로 할당한 메모리를 해제한다.
5. 포인터에 할당된 메모리를 2차원 배열처럼 사용하기
- 이번에는 포인터에 메모리를 할당하여 행 크기
3
, 열 크기4
인 2차원 배열처럼 사용해 보자.
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일
int main()
{
int **m = malloc(sizeof(int *) * 3); // 이중 포인터에 (int 타입 포인터 크기 * 행 크기)만큼
// 동적 메모리 할당. 배열의 행
for (int i = 0; i < 3; i++) // 행 크기만큼 반복
{
m[i] = malloc(sizeof(int) * 4); // (int 타입 크기 * 열 크기)만큼
// 동적 메모리 할당. 배열의 열
}
m[0][0] = 1; // 행 인덱스 0, 열 인덱스 0인 요소에 값 할당
m[2][0] = 5; // 행 인덱스 2, 열 인덱스 0인 요소에 값 할당
m[2][3] = 2; // 행 인덱스 2, 열 인덱스 3인 요소에 값 할당
printf("%d\n", m[0][0]); // 행 인덱스 0, 열 인덱스 0인 요소에 값 출력
printf("%d\n", m[2][0]); // 행 인덱스 2, 열 인덱스 0인 요소에 값 출력
printf("%d\n", m[2][3]); // 행 인덱스 2, 열 인덱스 3인 요소에 값 출력
for (int i = 0; i < 3; i++) // 행 크기만큼 반복
{
free(m[i]); // 2차원 배열의 열 공간 메모리 해제
}
free(m); // 2차원 배열의 행 공간 메모리 해제
return 0;
}
// 1
// 5
// 2
- 먼저 다음과 같이 이중 포인터에 2차원 배열의 행 공간에 해당하는 메모리를 할당한다.
- 이때 행 공간에는 값이 들어가지 않고 열 공간의 메모리 주소가 들어간다.
- 따라서
sizeof(int)
가 아닌sizeof(int *)
처럼 포인터의 크기를 구한 뒤 행 크기3
을 곱해 준다.
- 이중 포인터에 2차원 배열의 행 공간에 해당하는 메모리를 할당하는 모습을 그림으로 표현하면 다음과 같이 된다.
- 이제 행 크기만큼 반복하면서 2차원 배열의 열 공간에 해당하는 메모리를 할당한다.
- 열 공간에는
int
타입 숫자가 들어갈 것이므로sizeof(int)
에 열 크기4
를 곱해 준다.
for (int i = 0; i < 3; i++) // 행 크기만큼 반복
{
m[i] = malloc(sizeof(int) * 4); // (int 타입 크기 * 열 크기)만큼
// 동적 메모리 할당. 배열의 열
}
- 이중 포인터에 2차원 배열의 열 공간에 해당하는 메모리를 할당하는 모습을 그림처럼 표현하면 다음과 같이 된다.
- 즉,
m
은pointer to pointer to int
이므로int **m
으로 선언하고m[0]
,m[1]
,m[2]
는point to int
이므로int *
가 들어간다. - 마지막으로
m[0][0]
,m[0][1]
등은int
만 들어간다.
- 이제 2차원 배열을 사용하듯이
[][]
에 행 인덱스, 열 인덱스를 지정하여 값을 할당하거나 가져올 수 있다.
m[0][0] = 1; // 행 인덱스 0, 열 인덱스 0인 요소에 값 할당
m[2][0] = 5; // 행 인덱스 2, 열 인덱스 0인 요소에 값 할당
m[2][3] = 2; // 행 인덱스 2, 열 인덱스 3인 요소에 값 할당
printf("%d\n", m[0][0]); // 행 인덱스 0, 열 인덱스 0인 요소에 값 출력
printf("%d\n", m[2][0]); // 행 인덱스 2, 열 인덱스 0인 요소에 값 출력
printf("%d\n", m[2][3]); // 행 인덱스 2, 열 인덱스 3인 요소에 값 출력
- 포인터를 다 사용했다면 먼저 열 공간에 해당하는 메모리부터 해제한다.
- 그러고 나서 행 공간에 해당하는 메모리를 해제한다.
free(m);
처럼 행 공간에 해당하는 메모리를 먼저 해제해 버리면 열 공간 메모리를 해제할 수 없으므로 주의한다.
for (int i = 0; i < 3; i++) // 행 크기만큼 반복
{
free(m[i]); // 2차원 배열의 열 공간 메모리 해제
}
free(m); // 2차원 배열의 행 공간 메모리 해제
- 즉, 메모리를 할당할 때 행 → 열 순서로 할당했으므로 해제할 때는 반대로 열 → 행 순서로 해제한다.
6. 입력한 크기만큼 메모리를 할당하여 포인터를 2차원 배열처럼 사용하기
- 이제 사용자가 입력한 만큼 메모리를 할당하여 포인터를 2차원 배열처럼 사용해 보자.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일
int main()
{
int row, col;
scanf("%d %d", &row, &col);
int **m = malloc(sizeof(int *) * row); // 이중 포인터에 (int 타입 포인터 크기 * row)만큼
// 동적 메모리 할당. 배열의 행
for (int i = 0; i < row; i++) // 행 크기만큼 반복
{
m[i] = malloc(sizeof(int) * col); // (int 타입 크기 * col)만큼
// 동적 메모리 할당. 배열의 열
}
for (int i = 0; i < row; i++) // 행 크기만큼 반복
{
for (int j = 0; j < col; j++) // 열 크기만큼 반복
{
m[i][j] = i + j; // 2차원 배열의 각 요소에 i + j 값을 할당
}
}
for (int i = 0; i < row; i++) // 행 크기만큼 반복
{
for (int j = 0; j < col; j++) // 열 크기만큼 반복
{
printf("%d ", m[i][j]); // 2차원 배열의 인덱스에 반복문의 변수 i, j를 지정
}
printf("\n"); // 열 요소를 출력한 뒤 다음 줄로 넘어감
}
for (int i = 0; i < row; i++) // 행 크기만큼 반복
{
free(m[i]); // 2차원 배열의 열 공간 메모리 해제
}
free(m); // 2차원 배열의 행 공간 메모리 해제
return 0;
}
// 4 5 (입력)
// 0 1 2 3 4
// 1 2 3 4 5
// 2 3 4 5 6
// 3 4 5 6 7
- 먼저
scanf
로 행 크기와 열 크기를 입력받는다. - 그리고 입력받은 행 크기 변수
row
를 활용하여 이중 포인터m
에 2차원 배열의 행 공간 메모리를 할당한다.
- 입력받은 열 크기 변수
col
을 활용하여 2차원 배열의 열 공간 메모리를 할당한다.
for (int i = 0; i < row; i++) // 행 크기만큼 반복
{
m[i] = malloc(sizeof(int) * col); // (int 타입 크기 * col)만큼
// 동적 메모리 할당. 배열의 열
}
- 행, 열 크기가 고정되어 있지 않으므로 사용자에게 입력받은
row
,col
을 활용하여 행과 열을 반복하면서 값을 할당한다. - 여기서는 2차원 배열(메모리)의 각 요소에
i
와j
를 더한 값을 할당했다. - 값 할당이 끝났으면 다시 행과 열을 반복하면서 2차원 배열의 값을 출력한다.
for (int i = 0; i < row; i++) // 행 크기만큼 반복
{
for (int j = 0; j < col; j++) // 열 크기만큼 반복
{
m[i][j] = i + j; // 2차원 배열의 각 요소에 i + j 값을 할당
}
}
for (int i = 0; i < row; i++) // 행 크기만큼 반복
{
for (int j = 0; j < col; j++) // 열 크기만큼 반복
{
printf("%d ", m[i][j]); // 2차원 배열의 인덱스에 반복문의 변수 i, j를 지정
}
printf("\n"); // 열 요소를 출력한 뒤 다음 줄로 넘어감
}
- 마지막으로 입력받은 행 크기만큼 반복하면서 열 공간 메모리를 해제한다.
- 그러고 나서 행 공간 메모리를 해제한다.
for (int i = 0; i < row; i++) // 행 크기만큼 반복
{
free(m[i]); // 2차원 배열의 열 공간 메모리 해제
}
free(m); // 2차원 배열의 행 공간 메모리 해제