공부했던 자료 정리하는 용도입니다.

재배포, 수정하지 마세요.

 

 


 배열 

  1. 1차원 배열
    • 배열 선언 및 초기화
    • 배열 접근
      * 배열 접근 시 주의사항
    • 배열의 크기와 길이
      * 배열의 최대 크기
    • 배열을 이용한 문자열 변수의 표현
      * 공백과 null(\0)
      * scanf 함수를 이용한 문자열의 입력
  2. 다차원 배열
    • 2차원 배열 선언 및 초기화
    • 2차원 배열 접근
      * 2차원 배열의 크기, 행, 열 길이
  3. 가변 길이 배열(Variable-Length Array, VLA)

 


 

배열

  다수의 데이터를 저장하고 처리하는 경우에 유용하게 사용할 수 있다.

 

 

배열 선언 및 초기화

[자료형] [배열이름][길이정보] = {초기화 리스트};

// ex)
int arr1[7];				// 초기화 하지않고 배열만 선언
int arr2[5] = {1, 2, 3, 4, 5};		// 순차적으로 1, 2, 3, 4, 5로 초기화 됨
int arr3[] = {1, 2, 3, 4, 5, 6, 7};	// 초기화 리스트를 사용하면 배열의 길이를 생략할 수 있다.
int arr4[5] = {1, 2};			// 3, 4, 5번째 배열 요소는 0으로 채워진다.

// 변수를 통한 배열 길이 선언이 가능하지만 가급적 상수 사용을 권장!
int len = 20;
int arr[len];	// 가능

// 배열의 모든 요소를 0으로 초기화
int numArr[10] = {0};	// {0, }으로 해도 같음
  • 자료형 : 배열을 이루는 요소(변수)의 자료형
  • 배열 이름
  • 배열의 길이 : 배열의 길이를 선언할 때 변수를 이용할 수도 있다. 과거의 C표준에서는 배열의 길이 정보를 반드시 상수로 지정하도록 제한했기 때문에 범용적인 컴파일을 위해 가급적 배열 길이정보를 상수로 하는 것이 좋다.
  • 초기화 리스트(선택) : 기본 자료형 변수처럼 배열도 선언과 동시에 원하는 값으로 초기화할 수 있다. 배열 초기화를 목적으로 선언된, 중괄호 ``{ }``로 묶인 부분을 초기화 리스트라고 한다. 초기화 리스트를 사용하면 배열의 길이를 생략할 수 있으며, 배열의 길이보다 초기화 리스트의 요소 개수가 적을 경우 나머지는 @@0@@으로 채워진다.

      + 배열을 전역 변수로 초기화하지 않고 선언하면 전부 @@0@@으로 초기화된다.

 

 

 

 

배열 접근

// 배열 선언
int arr[3];

// 배열 접근
int arr[0] = 10;

// 반복문을 이용해서 배열의 모든 요소에 순차적으로 접근하는 것이 가능
for(int i = 0 ; i < 3 ; i++)
{
	printf("arr[%d] 입력 : ", i);
	scanf("%d", &arr[i]);		// 배열 요소를 대상으로 할 때는 변수와 마찬가지로 &연산자를 붙여야함
}

배열에 접근할 때 ``[ ]``연산자 사이에 배열의 위치정보를 명시하게 되는데 이를 인덱스(인덱스 값)라고 한다.

인덱스 값은 @@0@@부터 시작한다.

 

 

 

  + 배열 접근 시 주의사항 

#include <stdio.h>

int main(void)
{
	int arr[3];
	arr[0] = 1;
	arr[1] = 2;
	arr[2] = 3;
	arr[3] = 4;	// 오류여야 하지만 컴파일 에러가 일어나지 X
}

컴파일러는 배열 접근에 있어서 유효성 검사를 진행하지 않기 때문에 위와 같은 경우에도 컴파일 에러를 일으키지 않는다.

이 경우에는 할당하지 않은 메모리 공간을 침범하게 되므로 주의한다.

 

 

 

 

배열의 크기와 길이

#include <stdio.h>

int main(void)
{
	int arr1[5] = {1, 2, 3, 4, 5};	
	int arr2[] = {1, 2, 3, 4, 5, 6, 7};	// 초기화 리스트 사용시 배열길이 생략가능 
	int arr3[5] = {1, 2};
	
	printf("배열의 크기\n");
	printf("배열 arr1의 크기 : %d \n", sizeof(arr1));
	printf("배열 arr2의 크기 : %d \n", sizeof(arr2)); 
	printf("배열 arr3의 크기 : %d \n", sizeof(arr3)); 
	
	printf("\n배열의 길이\n");
	printf("배열 arr1의 길이 : %d \n", sizeof(arr1) / sizeof(int));
	printf("배열 arr2의 길이 : %d \n", sizeof(arr2) / sizeof(int));
	printf("배열 arr3의 길이 : %d \n", sizeof(arr3) / sizeof(int));
} 

배열의 이름으로 ``sizeof`` 연산자를 사용하면 byte단위의 배열 크기가 반환된다.

길이를 계산하고 싶으면 ``sizeof(arr1) / sizeof(int)`` 처럼 배열의 크기를 배열의 자료형으로 나눠야 한다.

 

실행 결과

 

 

  ■ 배열의 최대크기

  배열은 메모리의 스택에 생성되기 때문에 스택의 크기를 넘어서면 스택 오버플로우(stack overflow)가 발생하여 프로그램이 실행되지 않는다. 따라서 배열은 플랫폼에서 정한 스택의 크기보다 작게 선언해야하며 더 큰 크기를 사용하려면 ``malloc``함수로 동적 메모리를 할당해서 사용해야한다. (리눅스에서는 ##ulimit -a## 명령으로 스택 크기를 알아낼 수 있다.)

 

 

 

배열을 이용한 문자열 변수의 표현

char str[14] = "Good morning!";
char str[] = "Good";	// 배열 길이 생략 가능 !

  ``char``형 배열을 이용하면 문자열의 저장 및 변경이 가능해진다. `` " ``를 이용해서 문자열을 표현하며, 문자열을 선언과 동시에 지정하는 경우 배열의 길이를 생략하는 것도 가능하다.(컴파일러가 알아서 계산해서 배열 길이를 결정한다.) 문자열의 끝에는 @@\0@@이라는 특수문자(escape sequence)가 자동으로 삽입되어 실제 문자열의 길이보다 @@1@@더 길다. 따라서 문자열을 저장하는 목적으로 @@char@@형 배열을 선언할 경우 @@\0@@이 저장될 공간까지 고려해서 배열의 길이를 결정해야 한다. @@\0@@을 가리켜 널(null) 문자라고 하는데 문자열을 구분할 때 사용된다. 문자열에서 @@null@@문자는 매우 중요하며, @@null@@문자가 존재하지 않으면 컴파일러가 문자열로 인식하지 못한다. 

 

#include <stdio.h>

int main(void)
{
	char str[] = "Good morning!";
	printf("배열 str의 크기 : %d\n", sizeof(str));
	printf("널 문자 문자형 출력 : %c \n", str[13]);
	printf("널 문자 정수형 출력 : %d \n", str[13]);
	
	str[12] = '?';
	printf("문자열 출력 : %s", str);
	
	return 0;
} 

문자열을 변경하고, ``null``문자를 출력하는 예시이다.

 

실행 결과

 

 

#include <stdio.h>

int main(void)
{
	char str[50] = "I like C programming";
	printf("string : %s \n", str);
	
	str[8] = '\0';
	printf("string : %s \n", str);
	
	str[6] = '\0';
	printf("string : %s \n", str);
	
	str[1] = '\0';
	printf("string : %s \n", str);
	
	return 0;
} 

``printf`` 함수도 ``null``문자를 기준으로 문자열을 구분하기 때문에 ``%s``를 기반으로 문자열을 출력하면 ``null``문자의 위치에 따라 문장의 끝을 다르게 인식한다.

 

실행 결과

 

 

 

  ■ 공백과 null(\0)

#include <stdio.h>

int main(void)
{
	char nu = '\0';
	char sp = ' ';
	
	printf("null = %d \n", nu);
	printf("space = %d \n", sp);
	
	return 0;
} 

@@null@@ 문자(@@\0@@)의 아스키코드는 @@0@@, 공백의 아스키 코드는 @@32@@이므로 혼동하지 않도록 주의한다.

 

실행 결과

 

str[8] = '\0';
str[8] = 0;

``null``문자의 아스키 코드 값은 ``0``이므로 위의 두 문장은 같은 의미이다.

 

 

 

  ■ scanf 함수를 이용한 문자열의 입력

#include <stdio.h>

int main(void)
{
	char str[50];
	int idx = 0;
	
	printf("문자열 입력 : ");
	scanf("%s", str);	// 문자열을 입력받을 때는 &를 붙이지 X
	printf("입력 받은 문자열 : %s \n", str);
	
	printf("문자 단위 출력 : ");
	while(str[idx] != '\0'){
		printf("%c", str[idx]);
		idx++;
	}
	printf("\n");
	
	return 0;
} 

  C언어에서 표현하는 모든 문자열의 끝에는 @@null@@문자가 자동으로 삽입된다. 따라서 ``scanf`` 함수 호출을 통해 입력받는 문자열의 끝에도 ``null``문자가 삽입된다. @@scanf@@ 함수를 통해 문자열을 입력받을 때는 @@&@@연산자를 사용하지 않는다. 

 

실행 결과(공백이 없는 경우)

 

실행 결과(공백이 있는 경우)

  그러나 @@scanf@@ 함수는 공백을 기준으로 데이터를 구분 짓기 때문에 위와 같은 문장을 입력할 경우 ``He``, ``is``, ``my``, ``friend`` 총 4개의 문자열이 입력된 것으로 인식한다. 그래서 공백이 있는 문자열을 입력받을 때는 다른 함수를 사용해야 한다. 

 

 

 

 

 


다차원 배열

  2차원 이상의 배열을 다차원 배열이라고 한다. 논리적으로 2차원은 평면구조의 배열, 3차원은 직육면체 구조의 배열이다. C언어의 문법은 4차원 이상의 배열 선언을 문법적으로 허용하고 있지만 특정한 상황이 아니면 잘 사용하지 않는다. 2차원 배열도 메모리상에는 1차원 형태로 존재한다.

 

 

2차원 배열 선언 및 초기화

TYPE 배열이름[행의수][열의수];

// ex) 배열만 선언하는 경우
int arr1[3][4];
int arr2[2][6];

// ex) 선언과 동시에 초기화 하는 경우
int arr3[3][3] = {
		    {1, 2, 3},
                    {4, 5, 6},
                    {7, 8, 9}
                 };

int arr3[3][3] = {
		    {1},	// 비는 공간은 0으로 초기화됨
                    {4, 5},
                    {7, 8, 9}
                 };

// 한줄에 초기화 하는 것도 가능
int arr[3][3] = {1, 2, 3, 4, 5, 6, 7};

// 배열 크기를 생략할 경우 행의 길이만 생략가능
int arr1[][4] = {1, 2, 3, 4, 5, 6, 7, 8};
int arr2[][2] = {1, 2, 3, 4, 5, 6, 7, 8};

  2차원 배열은 1차원 배열과 달리 인덱스가 2개이다. 선언하면서 초기화하는 것도 가능한데, 한 줄로 선언해도 되고, 가독성 좋게 여러 줄에 해도 된다. 배열의 크기를 생략하는 경우는 배열의 행길이만 생략 가능하다.

 

 

 

 

2차원 배열 접근

  인덱스가 2개이므로 이중 ``for``문을 사용하면 2차원 배열에 순차적으로 접근하는 것이 가능하다.

 

 

  ■ 2차원 배열의 크기, 행, 열 길이

  2차원 배열 이름으로 sizeof 연산을 하게 되면 배열의 크기가 byte단위로 계산되어 나오므로(``int arr[3][4]``면 3 * 4 * 4 = 48byte) 행과 열의 길이를 각각 구해주어야 한다.

 

  •  행 길이  : sizeof(배열이름) / sizeof(배열의 행 한 줄)
    ex) sizeof(arr) / sizeof(arr[0]);
  •  열 길이  : sizeof(배열의 행 한 줄) / sizeof(자료형);
    ex) sizeof(arr[0]) / sizeof(int);

 

 

 

 

 


가변 길이 배열(Variable-Length Array, VLA)

자료형 *포인터이름 = malloc(sizeof(자료형) * 크기);

가변 길이 배열(프로그램 실행 중에 원하는 크기만큼 배열을 생성하는 기능)은 GCC에서는 지원하지만, Visual Studio 2015에서는 지원하지 않는다. 그래서 배열의 크기를 동적으로 지정하려면 포인터를 선언하고 메모리를 할당한 뒤 메모리를 배열처럼 사용해야 한다.

 

int numArr[10]	// int형 요소 10개를 가진 배열 선언
int *numPtr = malloc(sizeof(int) * 10);	// int 10개 크기만큼 메모리 할당

// 사용 방법은 같음
numArr[0] = 10;	// 배열을 인덱스로 접근하여 값 할당
numPtr[0] = 10;	// 포인터를 인덱스로 접근하여 값 할당

free(numPtr);	// 메모리 할당

배열과 메모리가 할당된 포인터는 생성방법만 다를뿐 값을 다루는 방법은 같다.

또한 포인터를 역참조한 값과 배열의 0번째 인덱스에 저장된 값은 같다.

 

 

  ■ 메모리를 할당해서 2차원 배열처럼 사용하기

  1. ``자료형 **포인터이름 = malloc(sizeof(자료형 *) * 세로크기)``로 세로 공간 메모리 할당
  2. 반복문으로 반복하면서 ``포인터[i] = malloc(sizeof(자료형) * 가로크기)``로 가로 공간 메모리 할당
  3. 반복문으로 반복하면서 ``free(포인터[i])``로 가로 공간 메모리 해제
  4. ``free(포인터)``로 세로 공간 메모리 해제

  메모리를 할당해서 2차원 배열처럼 사용하려면 위와 같은 과정을 거치면 된다. 메모리 할당 후에는 2차원 배열을 사용하던 것처럼 대괄호[ ]에 인덱스를 지정하여 값을 할당하거나 가져올 수 있다. 해제할때는 가로공간을 전부 해제한 후에 세로공간에 해당하는 메모리를 해제해야한다. (@@**@@포인터 하나만 @@free@@할 경우 가로공간에 할당되었던 메모리가 해제되지 않아 메모리 누수 발생)

 

 

 

'기타 > C' 카테고리의 다른 글

[C] 포인터와 배열, 함수  (0) 2020.05.07
[C] 포인터  (0) 2020.05.05
[C] 함수  (0) 2020.04.27
[C] 조건문, 반복문  (0) 2020.04.16
[C] printf, scanf 함수  (0) 2020.04.09

+ Recent posts