티스토리 뷰
토론
우리 클래스 학생들에 대하여 이름을 입력하면 해당 학생의 학년이 출력되는 프로그램을 만들고자 한다.
먼저 아래와 같은 질문에 대한 답을 한번 생각해보자.
1. 학생 수가 1명이라면, 이름과 학년을 각각 일반 변수 1개씩을 선언하여 저장하는 것이 좋을까? 아니면 1개짜리 배열을 선언하여 저장하는 것이 좋을까? 그 이유는 무엇인가?
2. 학생 수가 10명일 경우엔 어떠한가?
3. 학생 수가 500명일 경우에는?
학생애 대한 정보(이름, 학년)를 일반 변수, 배열에 담았을 경우의 차이에는 무엇이 있을까?
일단 변수와 배열의 정의를 살펴보자.
변수와 배열
변수 : 데이터를 저장할 수 있는 메모리 공간 혹은 그 공간에 명칭을 부여한 것
배열 : 변수를 여러개 선언할 때 하나의 명칭으로 관리하기 쉽도록 만든 변수 집합.
배열의 선언방법
1 | int arr[10]; | cs |
int : 데이터형
a : 배열 명
[10] : 배열의 크기
위와 같은 배열을 일차원 배열이라고 한다. 하지만 여러 이유로 배열을 병렬화 하여 사용하는 경우가 있다.
1 2 3 | int arr[10][10]; int arr[10][10][10]; | cs |
1열에 표시된 배열을 10 X 10 배열, 3열에 표시된 배열을 10 X 10 X 10 배열이라고 부른다.
각각의 배열엔 100, 1000개의 원소를 저장할 수 있다.
각각 이차원, 삼차원 배열이며 또 이처럼 병렬로 연결된 배열을 모두 다차원 배열이라고 부른다.
대부분 3차원 배열까지는 그림을 통해 이해할 수 있다.
다음은 6 X 7 인 2차원 배열을 나타낸 그림이다.
1 | int arr[6][7]; | cs |
다음은 3 X 6 X 7 인 3차원 배열을 나타낸 그림이다.
1 | int arr[3][6][7]; | cs |
배열의 메모리구조
배열은 같은 데이터형의 변수를 여러개 선언하고 싶을 떄 유용하다. 하나의 이름으로 여러개의 변수를 관리하기 편하고 메모리 공간을 연속적으로 사용하므로 메모리공간, 주소 관리애도 효율적이다. 아래 그림을 통해 배열의 메모리 구조를 확인해보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <stdio.h> int main() { printf("배열의 구조가 연속적임을 보이는 프로그램을 작성해보자.\n"); int arr[3][3]; // 3X3 배열 선언 for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { // 각 원소의 주소는 4Byte 만큼 떨어져있다. printf("&arr[%d][%d] = %d\n", i, j, (int)&arr[i][j]); } } for (int i = 0; i < 3; i++) { // arr[3]의 배열은 각 3개의 원소를 가지고 있으므로 12Byte 떨어져있다. printf("arr[%d] = %d\n", i, (int)arr[i]); } return 0; } | cs |
위 그림에서 '연속된 데이터 공간'의 의미에 대해 좀 이해가 갔다면 배열과 항상 함께 나오는 상수포인터(포인터상수)에 대해 알아보자.
배열 또한 데이터 공간이므로 메모리를 사용하고 그 메모리 공간에 주소를 가지고 있다.
우리는 위에서 '변수 = 메모리 공간의 이름'이라는 것을 배웠다. 하지만 배열에서도 이 규칙이 동일하게 적용될까?
아니다 배열은 변수와 달리 연속된 메모리 공간의 첫번째 주소를 지칭한다.
아래 코드를 살펴보고 결과를 예상해보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <stdio.h> int main() { int a = 1; int b[4] = { 1,2,3,4 }; printf("%d \n", a); printf("%d \n", b); return 0; } | cs |
위 코드에 대한 결과는 아래와 같다.
변수의 이름을 불러주었더니 1이라는 값을 가지고 튀어나왔다.
그런데 배열의 이름을 불렀더니 나오는 결과는 조금 이상하다.
이것이 배열의 이름이 가지는 '주소' 값이다.
배열의 원소에 접근하는 방법
다음의 그림을 보자.
int 형의 데이터는 4 Byte의 공간을 사용한다는 것을 알고 있을 것이다.
그리고 컴퓨터는 그 4 Byte의 공간중 첫 주소를 기억한다.
그렇다면 배열의 각 원소에는 에는 위와 같은 100, 104, 108, 112라는 주소를 통해 접근할 수 있다.
여기서 중요한 것은 배열의 공간이 연속적이라는 점!
연속적이기 때문에 해당 배열은 반복문을 통해 접근이 가능합니다.
하지만 선언하는 데이터 타입에 따라 1 Byte, 4 Byte, 8 Byte 등으로 메모리 공간이 변하기 때문에 배열은 좀 더 쉬운 규칙을 만들었습니다.
바로 +1, +2, +3, +N 처럼 처음에서 +N번 뒤에 있는 배열의 원소를 순서대로 지정 할 수 있는 기능,
이렇게 되면 int형 변수를 사용하다가 char, double 형으로 데이터형을 변경하여도 같은 동작을 수행하게 됩니다.
그렇다면 다음 예제를 통해 배열의 각 원소에 접근해 봅시다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #include <stdio.h> int main() { int arr[10]; for (int i = 0; i < 10; i++) { printf("arr[%d] = ", i); scanf("%d", arr + i); } printf("============================== \n"); for (int i = 0; i < 10; i++) { printf("arr[%d] = %d \n", i, arr[i]); } return 0; } | cs |
scanf의 인자에는 입력할 '데이터 공간의 주소'를 사용해야 한다.
하지만 여기서는 주소연산자를 사용하지 않았다. 우리는 이러한 형태를 사용한 적 있다. 바로 '포인터'
이제 상수 포인터의 의미가 와닿을 것이다. 이제 '상수'가 붙은 이유는 무엇일까?
상수라 함은 변하지 않는 불변량(값)이다. 배열의 이름 즉, 시작주소에는 다른 주소값을 대입할 수 없다. 상수적 특성을 가진 것이다.
이러한 이유로 아래 코드에서 arr에 주소를 대입하는 부분에 에러가 발생한다.
1 2 3 4 5 6 7 8 9 10 11 12 | #include <stdio.h> int main() { int arr[10]; int a; arr = &a; return 0; } | cs |
또 다른 의미로 배열의 길이에 상수값을 입력해야하는 특성을 꼽을 수 있다.
아래와 같은 코드를 작성하면 [k]에서 "상수 값을 입력해야 합니다."라는 에러가 발생한다.
1 2 3 4 5 6 7 8 9 10 11 12 | #include <stdio.h> int main() { int k; scanf("%d", &k); int arr[k]; return 0; } | cs |
컴파일 단계에선 메모리 스택의 크기가 결정되어 있어야 하는데 프로그램 실행 중 값을 받아오는 위와 같은 방식은 에러가 발생하는 것이다.
만약 프로그램 실행전에 메모리 크기를 바꾸어 사용할 계획이라면 다음과 같이 작성할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <stdio.h> #define LENGTH 10 int main() { int arr[LENGTH]; for (int i = 0; i < 10; i++) { printf("arr[%d] = ", i); scanf("%d", arr + i); } printf("============================== \n"); for (int i = 0; i < 10; i++) { printf("arr[%d] = %d \n", i, arr[i]); } return 0; } | cs |
위와 같은 방법으로 전처리기를 사용해 배열의 크기를 쉽게 관리할 수 있을 것이다.
배열은 그 메모리의 연속적인 특징 때문에 다음과 같은 코드에서 에러가 발생하지 않는다 . 이를 유의하여 사용하여야 한다.
1 2 3 4 5 6 7 8 9 10 | #include <stdio.h> int main() { int a[2]; a[2] = 3; return 0; } | cs |
구조체형 배열
구조체형 배열이라고 해서 어렵게 생각할 것 없다. 그냥 구조체라는 새로운 형태의 변수로 배열을 만든 것이다.
배열의 경우엔 선언한 형태의 데이터는 다룰 수 없기 때문에 학생의 성적을 입력하기엔 불편한 점이 있다.
A, B ,C 학생의 수학, 영어 성적을 나타낼 때, {A, 90, 80}, {B, 70, 30}, {C, 80, 100}과 같은 형태를 생각할 수 있다.
구조체는 이렇게 서로 다른 데이터형을 묶어 하나의 데이터 형을 선언하여 사용할 수 있다.
구조체 : 다양한 데이터형을 포함할 수 있는 사용자 정의 데이터형
구조체의 선언은 다음과 같다.
1 2 3 4 5 | struct Student { char name[10]; int math; int english; }; | cs |
struct라는 키워드를 사용해 선언하여 사용한다.
이후에는 일반적인 변수선언과 같이 [데이터형] [변수명]; 으로 선언하면 된다.
C++의 경우에는 위와 같이 선언이 가능하지만 C의 경우에는 struct [데이터형] [변수명]; 형태로 사용하여야 합니다.
1 | struct Student daerong; | cs |
이를 편하게 사용하기 위해 typedef 키워드로 데이터형을 정의내립시다.
1 2 3 | typedef struct Student STUDENT; STUDENT daerong; | cs |
또는
1 2 3 4 5 6 7 | typedef struct Student { char name[10]; int math; int english; } STUDENT; STUDENT daerong; | cs |
그렇다면 구조체형 배열을 선언하는 방법은?
1 | struct Student daerong[3]; | cs |
구조체 메모리구조
구조체의 메모리 구조는 배열과 유사하다, 연속된 공간의 데이터가 사용된다.
하지만, window 32bit 운영체제에서는 레지스터의 크기, 데이터 버스의 크기, 데이터 처리 단위, 포인터의 크기 등이 모두 4 Byte로 되어있어 4 Byte 단위로 처리하는 것이 더욱 빠릅니다. 때문에 컴파일 옵션에 따라 구조체를 해석할 때, 그 배열을 짝수 또는 4의 배수로 재구성하는 작업이 수행되게 됩니다.
위 내용을 숙지하고 다음의 배열을 봅시다.
1 2 3 4 5 6 7 | typedef struct Data { char c1; int d1; short h1; int d2; char c2; } DATA; | cs |
위와 같이 정의된 Data 구조체에서는 낭비되는 공간이 보입니다.
아래와 같이 순서를 변경하는 것으로 효율적인 관리가 가능해집니다.
1 2 3 4 5 6 7 | typedef struct Data { int d1; int d2; short h1; char c1; char c2; } DATA; | cs |
이렇게 구조체 내의 배치로도 구조체의 메모리 크기가 변할 수 있다는 것을 염두하고 사용하는 것이 좋겠습니다.
구조체형 배열의 메모리 구조
이제 구조체형 배열의 메모리 구조에 대해 쉽게 생각할 수 있을 것이다.
마치며,
이제 처음에 했던 질문에 대해 답해보자.
1. 학생 수가 1명이라면, 이름과 학년을 각각 일반 변수 1개씩을 선언하여 저장하는 것이 좋을까? 아니면 1개짜리 배열을 선언하여 저장하는 것이 좋을까? 그 이유는 무엇인가?
- 둘 다 동일하지만 1개짜리 배열을 선언할 경우 소스코드가 조금 더 길기 때문에 일반 변수를 사용하는 것이 좋을 것 같다.
2. 학생 수가 10명일 경우엔 어떠한가?
- 관리적 측면에서 배열을 선언하는 것이 좋아보인다.
- symbol table의 메모리도 적게 사용하며 일일히 주소를 찾아가는 과정이 적어져 더 빠른 프로그램을 구현할 수 있다.
3. 학생 수가 500명일 경우에는?
- 배열로 선언하자.
'프로그래밍 > C' 카테고리의 다른 글
[자료구조] 2일차_과제(180316) - 오목 (0) | 2018.03.16 |
---|---|
[창의적it 프로그래밍] 1차 과제 -구조체- (0) | 2018.03.13 |
[자료구조] 1일차_생각해 볼 내용(180312) (0) | 2018.03.08 |
[자료구조] 1일차_과제(180309) (0) | 2018.03.06 |
[자료구조] 1일차_실습(180309) (1) | 2018.03.06 |
- Total
- Today
- Yesterday
- oracle
- Gatsby.js
- MySQL
- vue
- DevOps
- PostgreSQL
- nuxt.js
- Angular
- vue.js
- REACT
- aws
- RDBMS
- svelte
- 이진탐색 #중복
- Azure
- Next.js
- Quasar
- Cloud
- nosql
- node.js
- alpine.js
- SQLite
- Remix
- gcp
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |