제 18강) 배열과 포인터 |
오늘은 배열을 배우고 포인터를 한 번 더 배웁니다.
포인터와 배열은 일심동체이기 때문이죠.
1) 변수의 집합, 배열 |
배열은 변수의 집합입니다.
int num1, num2, num3;
이렇게 변수 3개를 따로따로 선언했던 것을
int arr[3];
배열은 한 번에 선언을 가능케 합니다.
자료형 배열이름[배열의_크기];
자료형 배열이름[배열의_크기] = { 초기값 };
배열은 위와 같이 변수처럼 선언을 하지만 중괄호 사이에 배열의 크기를 지정하게 됩니다.
int arr[3];
int arr[3] = { 1, 2, 3 };
이렇게 사용이 가능케 됩니다.
이렇게 배열을 선언하게 되면 "배열의 크기"만큼 변수가 선언되지만 그 시작은 0부터 입니다.
int arr[3];
// -> arr[0], arr[1], arr[2]
int arr[3] = { 1, 2, 3 };
// -> arr[0] = 1, arr[1] = 2, arr[2] = 3
만약에 배열의 크기가 n으로 지정했다면 배열은 0부터 n-1까지 생성됩니다.
다음은 배열을 이용한 예제입니다.
#include <stdio.h>
int main()
{
int nums[4] = {1, 2, 3, 4};
for (int i = 0; i < 4; i++)
printf("nums[%d] = %d \n", i, nums[i]);
return 0;
}
길이가 4인 int형 배열 nums를 선언하여 값 1, 2, 3, 4를 넣었습니다.
그리고 for문을 이용하여 배열의 값을 출력합니다.
nums[0];
nums[2];
위 예제에서 봤듯이 배열의 괄호안에 접근할 위치를 넣어서 해당 값에 접근합니다.
#include <stdio.h>
int main()
{
int nums[4];
for (int i = 0; i < 4; i++)
{
printf("nums[%d] 입력: ", i);
scanf("%d", &nums[i]);
}
printf("\n");
for (int i = 0; i < 4; i++)
printf("nums[%d] = %d \n", i, nums[i]);
return 0;
}
위의 예제는 for문을 이용하여 배열에 직접 값을 넣고 그 결과를 확인하는 예제입니다.
2) 배열 파헤치기 |
1에서 잠깐 봤듯이 배열은 처음에 설정된 길이를 기준으로 선언이 됩니다.
int arr[4];
// arr[0], arr[1], arr[2], arr[3]
이렇게 n만큼 길이를 설정했다면 0 ~ n-1까지 배열이 생성됩니다.
그리고
#include <stdio.h>
int main()
{
int nums[4] = {1, 2, 3, 4};
for (int i = 0; i < 4; i++)
printf("nums[%d] = %d, address = %p \n", i, nums[i], &nums[i]);
return 0;
}
배열은 연속된 공간에 선언됩니다.
위의 nums 배열은 int형 배열로 선언되었기 때문에 int 크기인 4바이트만큼 떨어져서 연속적으로 할당된 것을 볼수 있습니다.
그런데 배열이 다음과 같이 선언되어있다고 하면 어떻게 될까요?
int arr[5] = { 1, 2 };
int arr[] = { 1, 2 };
위와 같이 뭔가 부족해보게 되어있으면 어떻게 될까요?
int arr[5] = { 1, 2 };
// -> int arr[5] = { 1, 2, 0 ,0 ,0 };
int arr[] = { 1, 2 };
// -> int arr[2] = { 1, 2 };
자동적으로 1번의 경우에는 길이보다 적에 값이 선언되어 있으면 선언되어 있지 않는 곳에는 0으로 채워집니다.
2번의 경우에는 길이가 정해져있지 않은 경우에는 선언된 값의 개수만큼 길이가 정해집니다.
3) 배열의 오류 - 배열 범위 |
배열을 사용하면 많이 겪는 오류가 하나 있는데 바로 "배열 범위를 벗어남(Out of Bounds)"이라는 오류입니다.
#include <stdio.h>
int main()
{
int nums[4] = {1, 2, 3, 4};
nums[4] = 5; // 오류 발생(배열 크기를 벗어남)
return 0;
}
바로 위 예제의 6번줄 처럼 길이는 4로(0~3) 지정되어 있는데 범위를 넘어서는 nums[4]에 접근하려고 하는 경우입니다.
위 예제와 같이 직접 접근(nums[4])하여 오류가 나는 상황은 극히 드물고 for문과 같이 변수를 이용하여 배열에 접근할 때 많이 오류를 범합니다.
(반복자 i가 nums[i]처럼 되어 있을때 i가 범위를 벗어나는 숫자가 들어감으로써 오류를 일으킵니다.)
4) 배열의 오류 - 배열 크기 |
배열을 사용하면 많이 겪는 오류가 또 하나 있습니다.
이 오류는 처음 배열을 배우는 분들이 겪는 오류인데요. 바로 배열의 크기에 변수를 넣는 경우입니다.
#include <stdio.h>
int main()
{
int size;
printf("배열의 크기: ");
scanf("%d", &size);
int arr[size]; // 오류 발생!!
return 0;
}
위와같이 배열의 길이를 입력받아서 그 길이만큼 배열을 생성하려고 하는 경우에 오류가 생깁니다.
배열의 크기는 무조건 변수가 아닌 "상수"가 와야 합니다.
그런데 정말로 배열의 길이를 동적으로, 내가 원하는 크기만큼만 할당하고 싶다면?
나중에 "동적배열 할당" 단원에서 배울테니 일단은 넘어갑시다.
5) 배열과 포인터 |
이제 배열을 포인터에 접목시켜봅시다.
#include <stdio.h>
int main()
{
int arr[4] = {1, 2, 3, 4};
int* ptr = arr; // 포인터를 배열의 첫번째 인덱스에 연결(arr[0])
return 0;
}
포인터를 배열에 연결시킬때는 위와 같이 연결합니다.
이때 주의해야할 사항은 배열 전체가 포인터와 연결되는 것이 아닌 배열의 첫번째 요소만 연결됩니다.
또한 변수에 포인터를 연결했을때와 동일하게 자료형이 같아야합니다.
즉, 위와 같은 상황입니다.
그럼 이제 포인터를 증가시켜보겠습니다.
포인터를 증가 또는 감소시키는 방법에는 2가지가 있습니다.
printf("*(ptr + 1): %d \n", *(ptr + 1));
// 포인터의 주소에 1을 더한다.
printf("*ptr + 1: %d \n", *ptr + 1);
// 포인터가 가리키는 값의 결과에 1을 더한다.
이때 포인터의 주소에 1을 더하는 것은 해당 포인터의 자료형에 맞게 1을 더하게 됩니다.
1을 더한다는 의미는 포인터가 다음 주소로 이동한다는 뜻입니다.
(int형 포인터가 가리키는 이전주소가 0004 였다면 여기에 1을 더하면 0008이 됩니다.)
#include <stdio.h>
int main()
{
int nums[4] = {1, 2, 3, 4};
int* ptr = nums; // 포인터를 배열의 첫번째 인덱스에 연결(nums[0])
printf("*ptr: %d \n", *ptr); // arr[0]
printf("*(ptr + 1): %d \n", *(ptr + 1)); // arr[1]
printf("*(ptr + 2): %d \n", *(ptr + 2)); // arr[2]
printf("*(ptr + 3): %d \n", *(ptr + 3)); // arr[3]
return 0;
}
위의 예제는 포인터의 주소에 1~3까지 더한 결과를 보여주는 예제입니다.
위와 같이 단순 더하기 계산이 된다면 선후행증감연산도 된다는 소리겠죠?
#include <stdio.h>
int main()
{
int nums[4] = {1, 2, 3, 4};
int* ptr = nums; // 포인터를 배열의 첫번째 인덱스에 연결(nums[0])
printf("*ptr: %d \n", *ptr);
printf("*(++ptr): %d \n", *(++ptr));
printf("*(++ptr): %d \n", *(++ptr));
printf("*(++ptr): %d \n", *(++ptr));
return 0;
}
6) 배열을 매개변수로 넘겨보자. |
이제 배열을 매개변수로 넘겨서 사용해봅시다.
#include <stdio.h>
#include <string.h>
int sumArr(int* arr, int length)
{
int total = 0;
for (int i = 0; i < length; i++)
{
total += arr[i];
}
return total;
}
void printArr(int* arr, int length)
{
printf("배열: ");
for (int i = 0; i < length; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main(void)
{
int arr[] = { 1, 2, 3, 4 };
printArr(arr, sizeof(arr) / sizeof(int));
printf("배열의 총합: %d\n", sumArr(arr, sizeof(arr) / sizeof(int)));
return 0;
}
이때 배열은 포인터 형식으로 넘겨지며 배열의 길이를 꼭 넣어주어야 합니다.
배열의 길이를 넘어온 배열을 이용하여 알 수 없기 때문입니다.
int sumArr(int* arr, int length) {}
int sumArr(int arr[], int length) {}
포인터를 배열로 해줘도 됩니다.
(같은 말입니다.)
7) 배열 연산 |
배열에 대해서 어느 정도 배웠으니 배열 주소에 관련하여 이야기해봅시다.
#include <stdio.h>
int main()
{
int arr[4] = { 1, 2, 3, 4 };
return 0;
}
위와 같은 배열이 있다고 합시다.
그럼 위의 배열의 첫번째 원소의 주소가 0000이라고 한다면 나머지 원소의 주소는
arr[0] : 0000
arr[1] : 0004
arr[2] : 0008
arr[3] : 0012(000C)
위와 같게 됩니다.
4만큼 증가하지요?
이유는 arr배열은 "int형"이기 때문에 4Byte 씩 늘어나게 됩니다.
그럼 배열의 주소에 직접 1을 더하면 어떻게 될까요?
#include <stdio.h>
int main()
{
int arr[4] = { 1, 2, 3, 4 };
printf("arr[0] : %p\n", &arr[0]);
printf("arr[0] + 1 : %p arr[1] : %p\n", &arr[0] + 1, &arr[1]);
printf("arr[0] + 2 : %p arr[2] : %p\n", &arr[0] + 2, &arr[2]);
printf("arr[0] + 3 : %p arr[3] : %p\n", &arr[0] + 3, &arr[3]);
return 0;
}
바로 다음 주소를 가리키게 됩니다.
그래서 arr[0] + 1은 arr[1]을, arr[0] + 2는 arr[2]를 가리키게 됩니다.
여기서 더하는 값은 자료형에 따라서 4바이트씩, 2바이트씩 변경되어 주소에 더해지게 됩니다.
다음시간에는 |
오늘은 배열에 대해서, 포인터와 연계지어서 배웠습니다.
다음 시간에는 배열을 이용한 문자열에 대해서 배워보도록 하겠습니다.
'Study > C언어' 카테고리의 다른 글
처음하시는 분들을 위한 C언어 기초강의 시즌2 - 20 [함수 포인터] (0) | 2018.10.17 |
---|---|
처음하시는 분들을 위한 C언어 기초강의 시즌2 - 19 [문자열] (0) | 2018.10.17 |
처음하시는 분들을 위한 C언어 기초강의 시즌2 - 17 [포인터] (0) | 2018.10.17 |
처음하시는 분들을 위한 C언어 기초강의 시즌2 - 07 [입력문과 출력문(printf, scanf, scanf_s)] (0) | 2018.10.17 |
처음하시는 분들을 위한 C언어 기초강의 시즌2 - 08 [변수에 대한 추가적인 이야기] (0) | 2018.10.17 |