Algorithm/C언어

[ 9. C언어의 기본 - 1차원 배열 ]

대시 2021. 10. 12. 10:44

이번 포스팅은 배열에 대해서 알아보도록 하겠습니다.

 

배열 (Array)

배열(array)은 같은 타입의 변수들로 이루어진 유한 집합으로 정의할 수 있다.

배열을 구성하는 각각의 값을 배열 요소(element)라고 하며, 

배열에서의 위치를 가리키는 숫자는 인덱스(index)라고 한다.

 

C언어에서 인덱스는 언제나 0부터 시작하며, 0을 포함한 양의 정수만을 가질 수 있다.

 

배열은 같은 종류의 데이터를 많이 다뤄야 하는 경우에 사용할 수 있는 가장 기본적인 자료 구조이다.

 

배열은 선언되는 형식에 따라 1차원 배열, 2차원 배열뿐만 아니라 그 이상의 다차원 배열로도 선언할 수 있다.

둘 이상의 변수를 동시에 선언하는 효과를 지닌다.

 

1차원 배열

<문법>

 

타입 배열이름 [배열길이];

 

- 타입은 배열 요소로 들어가는 변수의 타입을 명시한다.

- 배열 이름은 배열이 선언된 후에 배열로 접근하기 위해 사용된다.

- 배열의 길이는 해당 배열이 몇 개의 배열 요소를 가지게 되는지 명시한다.

 

 

※ C언어에서는 배열을 선언만 하고 초기화하지 않으면, 각 배열 요소에 아무런 의미를 가지지 않는

    쓰레기값이 저장되어 있게 된다.

    따라서 초기화되지 않은 배열은 사용하지 않도록 주의를 기울어야 한다.

 

예제

- 국영수 3개의 과목의 총 점수와 합계를 구하시오.

 

#include <stdio.h>
 
int main(){
 
  int i;
  int sum = 0;
  int grade[3]; // 길이가 3인 int형 배열 선언
 
  /* 배열의 초기화 */
 
  grade[0= 90;   // 국어 점수
  grade[1= 45;   // 영어 점수
  grade[2= 50;   // 수학 점수
 
  for ( i = 0; i < 3; i++)
  {
    sum += grade[i];
  }
  printf("국영수 과목 총 점수 합계는 %d점이고, 평균 점수는 %f점 입니다.\n"
  sum, (double)sum/3);
}
 
  
cs

 

밑에 처럼 선언과 동시에 초기화가 가능하다.

 

<문법>

타입 배열이름 [배열길이] = {배열요소1, 배열요소2, 배열요소3 .....};

 

int arr1[5] = { 1,2,3,4,5 }

int arr2[ ] = { 6,7,8,9,10 } // 초기화 리스트에 맞춰 자동으로 배열의 길이를 설정

int arr3[5] = { 1,2 }  // 1,2를 제외한 나머지 배열 요소는 모두 0으로 초기화 { 1, 2 , 0, 0, 0}

 

단, 초기화 리스트의 타입과 배열의 타입은 반드시 일치해야 한다.

만약에 초기화 리스트의 개수가 배열의 총 길이보다 적으면, 배열의 앞에서부터 차례대로 초기화된다.

이 때 초기화되지 못한 나머지 배열 요소는 모두 0으로 자동 초기화된다.

 

선언과 동시에 초기화하는 예제를 살펴보자. 코드는 위에와 동일하다.

 

#include <stdio.h>
 
int main(){
 
  int i;
  int sum = 0;
  int grade[3= {904550};
 
  for ( i = 0; i < 3; i++)
  {
    sum += grade[i];
  }
  printf("국영수 과목 총 점수 합계는 %d점이고, 평균 점수는 %f점 입니다.\n"
  sum, (double)sum/3);
}
 
cs

코드가 더 간결해짐 !^^!

 

배열의 특징

 

1) 배열의 길이를 선언할 때에는 반드시 상수를 사용해야 한다.

2) 배열 요소의 인덱스는 언제나 0부터 시작한다.

3) C 컴파일러는 배열의 길이를 전혀 신경 쓰지 않는다. 

 

배열을 길이와 크기를 구하자.

C언어에서 배열을 복사하거나 배열 요소에 특정 작업을 하고 싶을 때는 해당 배열이 차지하는

메모리의 크기를 정확히 알고있어야 한다.

 

C언어는 자료형마다 차지하는 메모리의 크기가 다른데,

메모리의 한 칸의 용량은 1바이트 (8bit)이다.

예를 들어 정수형(int) 자료형이 4바이트 라는 말은 생성시 메모리 네 칸을 차지한다는 뜻이다.

즉 32bit !!

 

또 예를 들어 정수형 변수 num을 네 칸 생성했을 경우 각 배열의 한 칸은 4byte이다.
따라서 num[4] 배열의 총 바이트 수는 16byte가 된다.

int num[4] = { 1,2,3,4 }

 

배열 사이즈 바이트값을 구해보자.

 

배열의 사이즈는 배열 개수에 자료형의 기본 바이트 수를 곱하면 된다.

또는 sizeof() 함수를 사용하면 된다.

 

1024개의 배열의 정수 자료형은 몇 바이트일까?

1024 x 4byte를 하면 4096 byte이고, sizeof() 함수로도 같은 결과가 나온다.

 

#include <stdio.h>
 
int main(){
 
  int num[1024= {0} ; // 1024개의 모든 배열에 정수 0을 대입
  int x;
 
  x = sizeof(num);
 
  printf("%d", x); // 1024 * int 4byte = 4096 byte
}

결괏값 : 4096
cs

문자 자료형은 배열 개수 * 1 byte, 실수(dobule) 자료형은 배열 개수 * 8 byte 이다.

#include <stdio.h>
 
int main(){
 
  char a[1024= {'0'}; // 1024개의 모든 배열에 문자 '0'을 대입
  double b[1024= {0}; // 1024개의 모든 배열에 실수 0 대입
 
  int A, B;
 
  A = sizeof(a); //문자 사이즈 저장
  B = sizeof(b); // 실수 사이즈 저장
 
  printf("%d\n", A); // 배열 1024 * char 1 byte = 1024 byte
  printf("%d\n", B); // 배열 1024 * double 8 byte = 8192 byte
}
cs

 

배열 개수 구하기

 

<수식>

배열 원소 개수 = sizeof(배열명) / sizeof(자료형);

#include <stdio.h>
 
int main(){
 
  int num[ ] = { 1,2,3,4,5,6,7,8,9,10,11 };
  int x;
 
  x = sizeof(num) / sizeof(int);
 
  printf("%d\n", x);
}

결괏값 : 11
 
cs

 

자료형마다 메모리 사이즈가 다르다는 것을 알고 있는 것이 중요하다!

 

1차원 배열과 포인터

예제를 통해 1차원 배열에서 포인터를 사용해볼 예정

 

예제1) 배열의 이름은 그 배열의 시작 주소이다.

#include <stdio.h>
 
int main(){
 
  int arr[3= { 102030};
 
  printf("%p %p %p\n", arr, arr + 0&arr[0]);
 
  printf("%p %p\n", arr + 1&arr[1] );
 
  printf("%p %p\n", arr + 2&arr[2] );
 
  printf("%lu %lu %lu\n"sizeof(arr), sizeof(arr+0), sizeof(&arr[0]));
  printf("%lu %lu %lu\n"sizeof(arr), sizeof(arr+1), sizeof(&arr[1]));
  printf("%lu %lu %lu\n"sizeof(arr), sizeof(arr+2), sizeof(&arr[2]));
 
  return 0;
 
  
cs

2) 배열 이름을 사용한 주소 내용 접근법

#include <stdio.h>
 
int main(){
 
  int arr[3= { 102030};
 
  printf("%d %d %d %d\n"*arr, *(arr + 0), *&arr[0], arr[0]);
 
  printf("%d %d %d\n"*(arr + 1), *&arr[1], arr[1]);
 
  printf("%d %d %d\n"*(arr + 2), *&arr[2], arr[2]);
 
 
  printf("%lu %lu %lu %lu\n"sizeof(*arr), sizeof(*(arr+0)), sizeof(*&arr[0]), 
         sizeof(arr[0]));

결괏값 :
10 10 10 10
20 20 20
30 30 30
4 4 4 4
  
 
cs

 

3) 1차원 배열과 1차원 포인터

#include <stdio.h>
 
int main(){
 
  int arr[3= { 102030 };
 
  int *= NULL;
 
  p = arr;
 
  printf("%p %p %p\n", p, p + 0&p[0]);
  printf("%p %p\n", p + 1&p[1]);
  printf("%p %p\n", p + 2&p[2]);
 
  
 
cs

 

4) 1차원 포인터를 통한 1차원 배열 요소 접근

#include <stdio.h>
 
int main(){
 
  int arr[3= { 102030 };
 
  int *= NULL;
 
  p = arr;
 
  printf("%d %d %d\n"*p, *(p+0), *&p[0]);
 
  printf("%d %d\n"*( p + 1 ), *&p[1] );
 
  printf("%d %d\n"*( p + 2), *&p[2]);
 
  return 0;
 
 
 결괏값 :
10 10 10
20 20
30 30
 
cs

 

5) 1차원 배열과 1차원 포인터

 

#include <stdio.h>
 
int main(void){
 
  int arr[3= { 102030 };
  int *= NULL;
  int i = 0;
 
  p = arr;
  
  for ( i = 0; i < 3; i++)
  {
    printf("%d %d %d\n"*(p + i), *&p[i], p[i]);
  }
 
  printf("------------\n");
 
  
  for ( i = 0; i < 3; i++)
  {
    printf("%d %d %d\n"*(arr + i), *&arr[i], arr[i]);
  }
  return 0;
 
 
cs

6) 포인터의 크기와 배열의 크기

 

#include <stdio.h>
 
int main(void){
 
  int arr[3= { 102030 };
  int *= NULL;
 
  p = arr;
 
  printf("%d %d %d\n", arr[0], arr[1], arr[2]);
  printf("%d %d %d\n"*(arr + 0), *(arr + 1), *(arr + 2));
 
  printf("%d %d %d\n", p[0], p[1], p[2]);
  printf("%d %d %d\n"*(p + 0), *(p + 1), *(p + 2));
 
  printf("배열의 크기 : %lu  포인터의 크기 : %lu\n"sizeof(arr), sizeof(p));
 
  return 0;
 

결괏값:
10 20 30
10 20 30
10 20 30
10 20 30

배열의 크기 :12
포인터의 크기 : 8
 
cs

여기서 결괏값을 보면 차이가 발생한 것을 볼 수 있다.

바로 배열과 포인터의 크기이다.

 

배열의 이름을 이용한 크기 계산에서는 배열의 크기가 int형 배열 요소 3개의 크기인 12바이트로 출력된게 보이고,

포인터를 이용한 크기 계산에서는 배열의 크기가 아닌 포인터 변수 자체의 크기가 출력되는 차이를 볼 수 있다.

 

1차원 배열은 여기까지 마무리하고 다음 포스팅은 다차원 배열을 다루도록 하겠습니다.