배열
배열은 같은 데이터 타입의 변수의 집합입니다.
배열은 배열의 원소들을 저장할때 실제로 인접한 메모리 위치를 사용합니다.
또한 배열은 초기화 할때 특별한 값으로 초기화하지 않는다면 0이나 NULL로 초기화 해주는게 좋습니다.
배열의 선언은 다음과 같습니다.
자료형 변수명[요소개수];
int a[10]; //int array
char b[10]; //char array (i.e. string)
위와 같이 int a[10]으로 선언한다면 데이터 타입이 int인 변수가 메모리 상에 연속적으로 10개가 붙어서 생성됩니다,
a[i]라고 한다면 i번째 인덱스에 접근한다는 의미입니다.
배열의 인덱스는 0부터 시작합니다. 따라서 마지막 인덱스는 배열을 선언할때 []에 써준 숫자보다 1 작은 수 입니다.
그렇다고 인덱스가 []의 숫자 이상인 위치에 접근을 아예 못하는것은 아닙니다.
접근은 가능하지만 알수 없는 결과가 나오기 때문에 사용하지 않습니다.
다음 표는 배열의 정의입니다.
이를 통해 선언이 어떨때 가능하고 어떨때 불가능한지 알아보겠습니다.
int age[] = {0, 1, 2, 3, 4}; //선언과 동시에 초기화하는 방법
int age[5] = {1}; //첫번째 원소를 1로, 나머지 원소들은 0으로 초기화
int age[]; //error
int hand[5]; hand[5] = {0, 1, 2, 3, 4}; //error
첫번째 문장의 경우 []에 배열의 요소 갯수를 기입하지 않았습니다. 하지만 에러는 발생하지 않습니다. 이런식으로 배열을 선언할 경우 컴파일러가 알아서 int형 변수 5개를 할당합니다.
두번째 문장의 경우 []에 5는 요소가 5개라는 것을 의미합니다. 이후에 {}안에 1만 써주었습니다.
이 때는 첫번째 요소를 1로 초기화하고 뒤에 남아있는 요소들은 0으로 초기화합니다.
세번째 문장과 같은 경우 에러가 발생합니다 배열을 초기화해주지도 않았을 뿐만 아니라 요소의 개수도 모르기 때문입니다.
네번쨰 문장도 에러가 발생합니다. 처음에 int hand[5]; 에서는 에러가 발생하지 않습니다.
하지만 뒤에 문장인 hand[5] = {0, 1, 2, 3, 4};에서는 에러가 발생합니다. 애초에 인덱스가 5인 곳에 데이터를 입력하는 것은 잘못되었고, 최초의 선언 이후 0,1,2,3,4 를 모두 입력하는것은 잘못되었습니다.
선언할때를 제외하고 변수명[i]를 입력하면 해당 인덱스에 접근하는것이지 초기화를 위와 같이 하는것은 불가능합니다.
예시를 한번 보겠습니다.
#include<stdio.h>
void main() {
int i;
int arr[5] = {10, 20, 30, 40, 50};
for (i = 0; i < 5; i++) {
printf("value of arr[%d] is %d \n", i, arr[i]);
}
}
반복문을 통해 배열의 요소에 하나씩 접근하여 값을 읽었습니다.
문자열 상수
C언어에서 쌍따옴표로 묶인 문자열을 문자열 상수라고 합니다. 상수는 영어로 constant라는 뜻인데 constant는 '불변' 이라는 의미를 담고 있습니다. 문자열 상수라고 하는 이유는 이런 문자열 안의 내용이 변경 불가능하기 때문입니다.
"Hello"는 문자열 상수입니다.
메모리에 저장될 때는 Hello\0으로 저장됩니다. \0은 NULL문자입니다.
문자열 상수가 저장된 위치의 주소는 'H'가 저장된 위치가 주소값으로 취급됩니다.
배열을 기반으로 문자열을 선언할 수 있습니다.
char string[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
char string[6] = "Hello";
char string [] = "Hello";
첫번째 문장은 배열의 선언과 동시에 문자상수 초기화 해주었습니다.
두번째 문장은 배열의 선언과 동시에 문자열 상수로 초기화 해주었습니다.
세번째 문장은 배열의 선언과 동시에 초기화하 컴파일러가 자동으로 인덱스를 6으로 할당합니다.
이 문장들은 모두 배열의 이름이 첫번째 요소의 주소를 가리킵니다.
예시들을 살펴보겠습니다.
#include <stdio.h>
void main () {
printf ("Hello");
char str[] = "hello";
printf(str);
printf("str: %s", str);
}
위 코드에서 알 수 있는 사실이 있습니다.
printf의 인자로 배열의 이름 자체를 쓸 수 있습니다.
만약 char str[6] = "hello"; 라고 했어도 아무 문제가 없었을 겁니다.
하지만 6대신 5를 쓴다면 이 배열은 문자열로 사용할 수 없습니다.
제일 마지막 요소에 \0(NULL 문자)가 없기 때문입니다.
문자열 상수는 항상 문자열뒤에 NULL문자가 있어야 하는데 인덱스를 5로 작성했다면 hello가 5칸의 자리를 모두 차지하고 마지막 NULL문자는 쓰이지 않기 때문에 문자열로 사용할 수 없습니다.
따라서 다음처럼 사용해야 합니다.
#include <stdio.h>
void main() {
char str[6] = {'h', 'e', 'l', 'l', 'o', '\0'};
printf("%s", str);
}
#include <stdio.h>
void main() {
char str[6] = "hello";
printf(str);
}
첫 번째 코드는 널 문자까지 입력해주는 것입니다. 여기서 널 문자를 입력하지 않고 비워 두더라도 컴파일러가 알아서 빈 공간을 널 문자로 초기화 합니다.
두 번째 코드는 배열의 인덱스가 6이기 때문에 hello다음에 널 문자까지 포함해서 총 6칸을 차지합니다 따라서 printf로 출력도 가능해집니다.
다차원 배열
지금까지 살펴본 배열은 1차원 배열이었습니다.
배열은 다차원 배열도 있습니다. 다차원 배열은 가장 간단한 형태가 2차원 배열입니다.
행렬로 생각해서 행과 열로 구분 지을 수 있습니다.
직관적 이해를 위해 그림을 통해 다음과 같이 표현했지만 메모리 상에는 이 모든 것들이 1열로 나열되어있습니다.
반복문에서 제일 바깥 반복에 대하여 내부 반복이 시행된것 처럼 배열이 나열 되는 순서도 제일 상위 인덱스에 대하여 하위 인덱스가 1씩 증가하면서 나열됩니다.
2차원 배열의 선언은 다음과 같습니다.
int a[10][20];
다음과 같이 선언하면 10행 20열 크기의 2차원 int 형 배열을 선언한 것입니다.
메모리에는 첫번째 인덱스의 크기 * 두번째 인덱스의 크기 * sizeof(데이터형)만큼의 byte가 저장됩니다.
2차원 배열의 초기화 방법을 보겠습니다.
int arr[3][3] = { {1, 1, 1}, {1, 1, 1}, {1, 1, 1} };
int arr[][] = { {1, 1, 1}, {1, 1, 1}, {1, 1, 1} };
int arr[3][3] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 };
1차원 배열과 매우 비슷한데 새로 추가된 점이 있다면 중괄호 안에서 또 중괄호로 묶인것들이 있다는 점입니다.
첫번째 문장의 경우 중괄호 속의 중괄호들은 각각의 행이라고 할 수 있습니다. 또 내부의 중괄호에서 순서대로 1열 2열 3열을 의미합니다.
세번째 문장의 경우 메모리상에 위치하는 순서대로 컴파일러가 알아서 값을 할당합니다. 이 점은 첫번째 문장에서 2차원 배열을 초기화한 방법과 다르지 않습니다.
예시를 보겠습니다.
#include <stdio.h>
int main(void){
int a[4][3];
int i, j;
for(i = 0; i < 4; i++)
for(i = 0; j < 3; j++)
a[i][j] = i * j;
for(i = 0;i < 4; i++){
for(j = 0; j < 3; j++){
printf("a[%d][%d] = %d ", i, j, a[i][j]);
}
printf("\n");
}
return 0;
}
2중 반복문을 통해 배열에 값을 할당하고 다시 2중 반복문을 통해 할당된 값을 출력해봤습니다.
'언어 > C언어' 카테고리의 다른 글
[C 언어] 10.포인터 (0) | 2024.04.14 |
---|---|
[C 언어] 9. 함수 (0) | 2024.04.14 |
[C 언어] 7. 제어문 (0) | 2024.04.14 |
[C 언어] call by value, call by reference 알아보기 (0) | 2024.04.14 |
[C 언어] 6. 변수와 자료형(더 자세히2) (0) | 2024.04.14 |