Programming/C, C++

코딩 자율 학습단 10일차 - 배열과 문자열

주눅 2024. 7. 2. 17:57

드디어 6장!!!

 

6장의 제목은 '배열과 문자열'이다.

이것도 참 많이 헤매었었던 기억이 난다 ㅜㅜ;; 자신 없음...

슬슬 어려운 것들이 나오기 시작해서 긴장된다

뭐 그래도 해봐야지 어쩌겠어


 

6장에서 만들 수 있는 프로그램은 '자라나라 머리카락'!!

주제가 참 잔인한 것 같기도 하다

 

총 네개의 약을 어떻게 조합해 가며

어떤 것이 진짜 발모제인지를 알아맞히는 게임이고,

이를 구현하기 위해 필요한 것이 바로 배열과 문자열이다.

 

 

가장 먼저 배열array이란,

동일한 자료형의 값 여러 개를 저장할 수 있는 연속된 저장 공간이다.

 

배열을 사용하기 위해서는 우선 배열을 선언해야만 한다.

배열을 선언할 때엔 자료형과 배열명, 배열 크기를 적어주어야 한다.

자료형 배열명[배열크기];

 

그럼 이를 이용해 직접 배열을 선언해 보자.

 

차량 3대가 연결된 3칸짜리 지하철이 있다고 치자.

1호차에는 30명, 2호차에는 40명, 3호차에는 50명이 탑승하고 있다.

 

따라서 배열 subway_array를 선언하는데,

정수 3개(30, 40, 50)를 저장하므로 자료형은 int, 배열의 크기는 3이다.

int subway_array[3];

 

이제 배열 subway_array에는 공간이 3개 존재하는데,

배열에선 변수 하나에 해당하는 공간을 요소라고 하며

배열의 요소는 인덱스Index라는 번호로 구분한다.

 

인덱스는 항상 0부터 시작하며,

크기가 n인 배열이라면 인덱스는 0부터 n-1까지 존재한다.

 

각 요소는 배열명에 인덱스를 붙인

subway_array[0], subway_array[1], subway_array[2]로 구분되며,

이는 각 요소의 값에 접근하기 위해 사용된다.

 

선언한 배열에는 다음과 같이 값을 넣어 초기화한다.

값을 넣을 때는 대입 연산자(=)를 사용한다.

#include <stdio.h>

int main(void) {
	int subway_array[3];

	subway_array[0] = 30; // [30] [ ] [ ] 
	subway_array[1] = 40; // [30] [40] [ ] 
	subway_array[2] = 50; // [30] [40] [50]

	return 0;
}

 

지금 subway_array 배열에는 다음과 같이 값이 저장되어 있다.

 

 

이어서, 배열에 제대로 값이 저장되었는지를 출력을 통해 확인해 보자.

 

#include <stdio.h>

int main(void) {
	int subway_array[3];

	subway_array[0] = 30; // [30] [ ] [ ] 
	subway_array[1] = 40; // [30] [40] [ ] 
	subway_array[2] = 50; // [30] [40] [50]

	for (int i = 0; i < 3; i++) {
		printf("지하철 %d호차에 %d명이 타고 있습니다.\n", i + 1, subway_array[i]);
	}

	return 0;
}

실행 결과

 

for문을 사용해 반복되는 코드를 간략히 적었다.

또, 인덱스는 0부터 시작하지만 지하철은 1호차부터 시작한다.

실제 호차와 출력이 일치할 수 있게끔 '지하철 %d호차' 부분에는 i + 1로 값을 표시했다.

 

실행 결과, 지정한 값이 제대로 배열에 저장되었음을 확인할 수 있었다.

 

+) 배열의 크기는 항상 상수로 선언해야만 한다.

배열의 선언에 변수를 사용할 수는 없다.

int size = 10;
int array[size]; //오류 발생

int array[10]; //이렇게 선언해야 한다

 


 

위의 예제를 작성하며 배열을 선언하고, 인덱스를 사용해

배열의 요소에 값을 하나하나 저장했다.

 

하지만, 다음과 같은 방법으로 배열을 선언하는 동시에 초기화할 수도 있다.

int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

 

마찬가지로, 배열에 값이 잘 저장되었는지 출력을 통해 확인해 보자.

#include <stdio.h>

int main(void) {

	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

	for (int i = 0; i < 10; i++) {
		printf("%d\n", arr[i]);
	}

	return 0;
}

실행 결과

 

실행 결과, 저장한 값 10개가 잘 출력되는 것을 확인할 수 있다!

 

그런데 배열을 선언하기만 하고, 초기화하지 않으면 어떻게 될까?

앞의 코드에서 값을 넣은 부분을 삭제한 후 다시 실행해 보자.

실행 결과

 

이상한 값이 10개 출력된다.

이러한 값을 더미 값dummy value 혹은 쓰레기 값garbage value라고 한다. 

배열을 초기화하지 않으면 더미 값이 할당된 메모리 공간 안을 차지하게 된다.

 

그렇다면 크기 10인 배열의 일부만 초기화한다면 어떻게 될까?

#include <stdio.h>

int main(void) {

	int arr[10] = { 1,2 }; //일부 값만 초기화

	for (int i = 0; i < 10; i++) {
		printf("%d\n", arr[i]);
	}

	return 0;
}

실행 결과

 

직접 초기화하지 않은 부분은 0으로 초기화되었다는 사실을 알 수 있다. 

 

즉, 배열을 선언하며 일부만 초기화했을 경우에는 나머지 값이 0으로 초기화되며,

하나도 초기화하지 않으면 모든 값이 더미 값이 된다. 

 

 

+) 배열을 선언할 때에는 배열의 크기를 꼭 포함해야 하지만,

배열을 선언하는 동시에 초기화한다면 대괄호 안에 배열 크기를 넣지 않아도 배열이 생성된다.

컴파일러가 값의 개수를 세어 자동으로 배열의 크기를 설정하기 때문이다.

int arr[] = { 1, 2 }; //이런 식으로 선언, 초기화해도 문제 없다

 

++) 실수형 배열은 다음과 같이 만든다.

int main(void) {
	float arr_f[5] = { 1.0f,2.0f,3.0f };

	for (int i = 0; i < 5; i++) {
		printf("%.2f\n", arr_f[i]);
	}

	return 0;
}

실행 결과

 

저장한 값이 1.00, 2.00, 3.00의 형태로 출력되었고,

값을 저장하지 않은 요소는 정수형 배열과 마찬가지로 0.00으로 출력되었다. 


 

다음으론 배열에 문자열을 저장하는 방법을 알아본다.

 

알아보기에 앞서,

문자는 글자 하나를 의미하며, 글자를 저장하기 위해선 char 형을 사용한다.

값은 작은따옴표로 표시하며, 출력할 때에는 %c 서식 지정자를 사용한다.

#include <stdio.h>

int main(void) {
	char c = 'A';
	printf("%c\n", c);

	return 0;
}

 

실행 결과

 

그렇다면 문자열은 어떻게 저장하고 출력해야 할까?

문자열은 여러 문자의 모임이니 값이 여러 개고, 따라서 배열에 저장할 수 있다.

 

문자열 'coding'을 배열에 저장한 후 출력해 보자.

 

자료형은 동일하게 char를 이용한다.

배열의 이름은 문자열String을 의미하는 str,

배열의 크기는 'coding'의 글자 수에 맞춰 6으로 선언한다.

여기에 문자열 coding을 넣어 초기화한다.

 

+) 문자는 작은따옴표로 감싸고, 문자열은 큰 따옴표로 감싼다.

#include <stdio.h>

int main(void) {
	char str[6] = "coding";

	printf("%s\n", str);
	return 0;
}

실행 결과

 

coding 까지는 제대로 출력되었지만, 뒤에 이상한 값이 붙어 나왔다 ㅠ.ㅠ

 

이는 배열의 공간이 부족해 문자열 끝에 널Null 문자가 포함되지 못해서 발생한 문제다.

널은 특수 문자로, '\0' 형태로 표현한다. 문자열의 끝을 알리는 역할을 한다.

 

따라서, 문자열을 선언할 때에는 글자 수 + 1로 배열의 크기로 선언해

마지막 칸에 자동으로 널 문자가 들어가게끔 해야만 한다.

 

실행 결과

 

문자열의 크기를 [7]로 늘렸더니 오류 없이 제대로 실행된다!

 

하지만 만약 배열의 크기를 직접 지정하지 않은 채 문자열을 저장하면 어떻게 될까?

배열의 크기를 비워둔 채 문자열을 저장한 후,

sizeof() 연산자를 통해 배열의 크기를 알아낼 수 있다.

 

sizeof() 연산자는 안에 넣은 배열, 변수, 자료형 등이 차지하는

메모리 공간의 크기를 바이트 단위로 알려준다. 

알파벳과 숫자는 메모리에서 1바이트를 차지한다.

 

#include <stdio.h>

int main(void) {
	char str[] = "coding";
	printf("%s\n", str);

	printf("%d\n", sizeof(str));
	return 0;
}

실행 결과

 

실행 결과, sizeof()의 값이 7이라 출력되었다.

이는 coding의 6글자와 문자열의 끝을 나타내는 널 문자(\0)의 크기를 합산한 값이다.

 

배열의 크기를 지정하지 않으면 값의 개수에 맞춰 공간이 자동으로 생성되며,

이때 문자의 끝인 널 문자가 들어갈 공간도 자동으로 생성된다.

 

 

그럼 문자열에 한글을 저장하면 어떻게 될까?

kor이라는 배열 안에 '나도코딩'을 저장해 보자.

#include <stdio.h>

int main(void) {
	char kor[] = "나도코딩";
	printf("%s\n", kor);

	printf("%d\n", sizeof(kor));
	return 0;
}

실행 결과

 

나도코딩은 네 글자고, 널 문자를 포함해도 총 다섯 글자인데

배열의 크기는 9로 출력되었다.

 

이는 알파벳과 한글이 차지하는 저장 공간의 크기가 서로 다르기 때문이다.

알파벳과 숫자는 한 글자가 1바이트를 차지하지만, 한글은 한 글자가 2바이트를 차지한다.

이는 복잡한 한글의 구조를 1바이트로 표현할 수 없기 때문으로,

한글은 한 글자를 표현하는 데에 2바이트가 필요하다.

 

즉, '나도코딩' (4개 글자 x 2바이트) + '\0' = 9로,

배열의 크기가 9인 것!!

 


 아스키코드란, 미국표전협회에서 제시한 국제 표준 코드 체계다.

아스키코드는 문자 하나를 7비트로 표현한다.

 

아스키 코드는 문자를 출력할 때 서식 지정자를 '%d'로 지정해 쉽게 확인해 볼 수 있다.

#include <stdio.h>

int main(void) {
	printf("%c\n", 'a');
	printf("%d\n", 'a');
	printf("%c\n", 'b');
	printf("%d\n", 'b');
	printf("%c\n", 'A');
	printf("%d\n", 'A');
	printf("%c\n", '\0');
	printf("%d\n", '\0');
	printf("%c\n", '0');
	printf("%d\n", '0');
	printf("%c\n", '1');
	printf("%d\n", '1');

	return 0;
}

실행 결과

소문자 a, b와 대문자 A, 널 문자, 0과 1의 아스키코드 값을 확인해 보았다.

 

이번에는 거꾸로 0에서 127 사이의 아스키코드에 해당하는 문자를 확인해 보자.

for문을 사용해 변수 i의 값을 1씩 증가시키며 그 값을 출력한다. 

#include <stdio.h>

int main(void) {
	for (int i = 0; i < 128; i++) {
		printf("아스키코드 값 %d : %c\n", i, i);
	}

	return 0;
}

 

실행 결과

 

중간에 소리가 나는데, 이는 아스키코드 값 7에 비프음 코드가 있기 때문이라고 한다

신기... +ㅅ+

 

조금 다른 얘긴데 이걸 보니 반복문이 정말 편리한 거구나 싶다... 


 

이어서, 11일 차에는 이번에 배운 내용들을 토대로

'자라나라 머리카락' 게임을 만들어본다.

 

시작하기 전에 복습 열심히 해야 할 듯....

공부 일단 끝!