요즘 동영상을 이용한 학습이 전세계적으로 유행이다. Khan Acamedy로부터 시작된 열풍이 이제는 대학으로까지 번지고 있다. Coursera, Edx, Udemy 등의 영리 사이트들도 속속 생겨났다. 이들 사이트들은 해외 유명대학의 강의들을 제공하고 있다. 소문난 강의의 경우 수십만명의 사람들이 수강하기도 한다. 무료 강의도 있지만, 유료 강의들도 상당수다. 그만큼 듣는 사람들이 많아졌다는 것이다.
동영상 기반 학습의 장점
동영상을 이용한 학습은 여러 가지 장점들을 가지고 있다. 우선 반복해서 다시 볼 수 있다는 큰 장점이 있다. 수업시간에 놓친 것들, 이해가 잘 되지 않았던 부분들을 반복해서 볼 수 있다. 이렇게 하면 자기가 이해하는 속도에 맞춰서 진행할 수도 있다. 또 다른 장점은 뭐니뭐니 해도 시간과 장소의 제한을 받지 않는 것이다. 등하교길의 버스나 지하철, 혹은 기다리면서, 또는 잠깐 쉬면서 틈틈히 공부할 수 있다는 장점이 있다. 요즘같이 스마트폰이 광범위하게 사용되는 환경에서는 이러한 시공초월의 장점은 극대화된다.
이 글의 목적
이러한 장점을 가진 동영상이지만 제대로 학습할 줄 아는 학생들은 많지 않은 듯 하다. 특히 프로그래밍 관련 된 것을 동영상으로 배울 때는 더더욱 그런 것 같다. 그래서 여기에서는 동영상을 이용해서 프로그래밍을 공부하는 방법에 대해서 설명할까 한다. 물론 이 방법이 최선의 방법이 아닐 수도 있고, 더 좋은 방법이 있을 수도 있다. 하지만 이부분은 대부분의 사람들이 잘못 사용하고 있는 동영상 학습방법보다는 나을 것이다.
동영상공부방법의 잘못된 예 : 따라치기
가장 쉽게 저지르는 실수는 동영상을 보면서 코딩을 동시에 진행하는 것이다. 동영상에 나오는 프로그램을 그대로 따라 치는 식으로 학습한다. 이렇게 공부하는 것은 타자연습밖에는 되지 않는다. IDE같은 프로그래밍 환경 사용방법을 학습할 때는 어쩔 수가 없더라도, 동영상에 나오는 프로그램을 보면서 그대로 따라치는 것은 노력대비 효율성면에서 좋지 않다. 왜냐면 수동적인 공부가 되기 때문이다. 프로그래밍이란 정신작업인데, 그대로 따라치는 것은 육체노동에 불과하다. 키보드를 두드리는 순간 그나마 이해했던 많은 부분들이 뇌에서 빠져나가 버린다.
바람직한 동영상 공부방법
그럼, 어떻게 공부하는 것이 좋을까? 우선 동영상을 일정 분량만큼 보고 나서는 동영상을 더 이상 보지 않고, 혼자서 기억나는 대로 프로그램을 작성해 본다. 한 번에 끝까지 프로그램을 완성할 수 있으면 좋겠지만 쉽지는 않을 것이다. 그렇게 막혔을 때에는 동영상의 해당 부분을 다시 한 번 보면서 이해한다. 그리고 나서, 다시 프로그래밍을 할 때는 이전에 작성했던 부분들을 모두 지워야 한다. 이것이 중요하다. 다 지워야 한다. 깨끗한 백지상태에서 처음부터 시작해야 한다. 그렇게 하지 않으면, 동영상을 그대로 따라치는 것과 다르지 않기 때문이다.
바람직한 동영상 공부방법: 이 방법의 문제점
이 방법을 그대로 적용하려면 암기력이 엄청 좋아야 하는데, 사실 그건 불가능하다. 왜냐면 처음보는 함수이름, 사용법, IDE 사용방법 등을 보는 즉시 다 외워야 하기 때문이다. 특히 함수이름에 대소문자가 섞여있거나, 함수 인수가 굉장히 많을 때는 더더욱 힘들다. 동영상을 보지 않는 목적은 프로그램을 이해하기 위해서이지 순간 암기능력을 키우기 위해서가 아니다. 이런 의미에서 단순히 동영상을 보지 않는 것만이 최선은 아니다.
동영상 공부방법의 보완: 필기병행
동영상을 보면서 필기를 병행해야 한다. 낯선 함수 이름, IDE 사용법 등을 재빠르게 필기해두었다가 프로그래밍을 하면서 참조를 한다. 그렇다고 해서 프로그램을 그대로 베껴 놓으라는 것은 아니다. 어디까지 필기를 해둬야 할지는 본인이 판단해야 한다. 그 판단기준은 필기내용으로 인해 프로그래밍이 육체노동이 되지 않는 수준까지만 하면 된다. 너무 많은 필기를 해두면 육체노동이 될 것임은 뻔하다.
첨부터 끝까지 다 보려는 것의 잘못
동영상을 한 번 앉은 자리에서 모두 보겠다는 욕심도 쉽게 저지르는 실수다. 물론 시간과 자신의 집중력이 충분하다면 그렇게 하면 된다. 하지만 그런 방법은 학습을 쉽게 질리게 만든다. 그런 경험이 몇 번 반복되다 보면 학습하는 것 자체가 부담스럽다보니 아예 시작조차 못하는 경우도 있다.
해결방법: 틈나는 대로 봐야
동영상은 시간나는 대로 짬짬이, 볼 수 있는 장소에서 보면 된다. 물론 한자리에 앉아서 쭉 하는 것보다는 더 오래 걸리겠지만, 어쨌든 부담없이 시작할 수 있다. 하나의 학습주제는 일반적으로 10분 이내의 짧은 동영상 여러 개로 구성된다. 특히 요즘 핫한 coursera 등이 그렇게 구성된다.
변수 b가 저장한 값 100을 주소로 인식하고, 그 100번지에 저장되어 있는 값을 읽어오는 것이다.
포인터 변수 앞에 기호 *를 쓰면, 그 번지에 저장된 값을 읽거나 쓸 수 있다.
단, 헷갈리지 말자.
int *b; 이건 변수 b가 포인터라는 것을 정의하는 것이다.
포인터 변수를 만들 때도 기호 *를 사용한다.
*b = 10;
변수 b가 저장한 값 100을 주소로 인식하고, 그 100번지에 값 10을 쓰는 것이다.
기호 *을 붙여 값을 쓰는 용도로 사용한 것이다.
배운 것을 정리해보자.
1. a = 10;
2. b = &a;
3. *b = 20;
4. printf ("%d", a);
라인 1. 변수 a에 값 10을 저장한다.
라인 2. 변수 a에 매칭된 저장공간의 주소를 b에 저장한다.
라인 3. b에 저장된 주소는 a것이므로, a에다가 20을 저장한다.
라인 4. 위에서 a에 저장된 값이 20으로 바뀌었으므로 10이 아닌, 20을 출력한다.
포인터는 어디에 필요한가?
컴퓨터를 생각해보자.
키보드, 디스크, 비디오카드, USB메모리, CPU 등 여러 가지 장치들이 있다.
프로그램으로 이 장치 들에게 명령을 내리고 싶다면, 어떻게 할 수 있을까?
각 장치들은 고유의 주소를 가지고 있다.
이 주소들을 이용하여 명령을 내릴 수 있다.
예를 들어, 비디오카드가 100번지에 있다면, 100번지에 값을 읽거나, 써서 비디오카드에게 명령한다.
이를 가능하게 하는 것이 포인터다.
따라서 포인터를 아는 것은 시스템을 내 휘하에 두고 이러저러 명령을 내릴 수 있는 힘을 얻는 것이나 마찬가지다.
int *a
포인터 변수를 선언한 것이다.
하나하나 뜯어서 살펴보자.
a는 포인터 변수의 이름이다.
*: a 앞에 붙은 기호 *은 변수 a가 포인터임을 나타낸다. 기호 *이 없으면 포인터가 아니다.
int: 이것의 의미를 잘 이해해야 한다. 포인터 변수 a를 이용하면 어떤 주소 위치에 값을 쓸 수도 있고, 읽어올 수도 있다. 이 때 그 값의 형(type)을 나타낸다. 즉 변수 a를 이용하면 읽거나 쓰는 값은 int이고, 따라서 4바이트 단위로 읽거나 쓸 수 있게 된다. (32비트 운영체제일 경우에 말이다. 64비트 운영체제이고 int가 64비트이면 8 바이트 단위로 동작한다.)
int **a
포인터 변수이긴 하다.
여기에는 기호 *이 두 개가 붙었다.
이중 포인터라는 것이다.
이것의 의미도 하나하나 뜯어보자.
a: 포인터 변수의 이름이다.
* (오른쪽에서 첫 번째): 변수 a가 포인터임을 나타낸다.
int * (오른쪽에서 두 번째 *): 변수 a를 이용하여 읽고 쓸 수 있는 값의 데이터 형이 int *, 즉 정수 포인터
예제프로그램
변수 a를 만들고, 거기에다가 10을 넣었다.
포인터 변수 b를 선언했다.
포인터 변수 c를 선언했다.
b를 이용해서는 int값을 읽고 쓸 수 있으므로, b에는 변수 a의 주소를 넣는다.
c를 이용해서는 int *값을 읽고 쓰기 때문에, c에는 int 포인터 변수 b의 주소를 넣는다.
출력결과는 모두 10, 10, 10
첫 번째 10: 변수 a의 값을 출력한 것
두 번째 10: 포인터 변수 b를 이용하여, a의 주소를 이용해서 int값을 읽어서 출력했다.
세 번째 10: 이중 포인터 변수 c를 이용했다. **c == *(*c)로 이해하면 쉽다. *c는 b에 저장된 a의 주소를 읽는다. *(*c)는 a의 주소를 이용하여 a에 저장된 값을 읽는다.
malloc
메모리를 할당받는 데 사용하는 함수이다.
원하는 크기의 메모리 공간을 할당받을 수 있다.
이 함수의 인자는 할당받기를 원하는 크기이다.
크기는 바이트 단위이다.
malloc(100) : 100 바이트 크기의 공간할당을 요청하는 것이다.
이 함수의 반환값은 주소이다.
할당된 공간의 시작주소이다.
반환값이 주소이므로 이 주소는 포인터 변수에 저장해야 한다.
malloc casting
malloc함수의 원형은 아래와 같이 생겼다.
void * malloc ( int size)
반환형이 독특하게 생겼다.
void *
생긴 것을 봐서는 포인터이다.
왜냐면 *이 붙었으니까.
이것으로부터 유추해보면, 포인터 즉 주소값을 반환한다는 뜻이다.
좀 더 추리해보면, 할당받은 공간과 관련된 주소일 것이다.
정확하게 말하자면, 할당받은 공간의 시작주소가 반환값이 된다.
그러면 void는 무슨 뜻인가?
void의 사전적인 의미는 '비어있는' '없는' '무효' 이다.
데이터 형이 없다는 뜻으로 생각하면 된다.
왜 void, 데이터형을 없는 것으로 해 놨을까?
그건 공간을 마음대로 활용할 수 있도록 해 주기 위해서이다.
공간에 int 값을 쓰거나 읽을 용도로 할당받는 경우도 있고,
char 값 용도로 쓸때도 있다.
그러니 데이터형을 일단 없다고 하고,
실제로 할당받는 순간에 그 형을 정해서 쓰기 위해서 void로 해 놓은 것이다.
공간을 할당받는 순간에 데이터형을 결정하다. casting
int *k;
k = (int *)malloc(100);
char *m;
m = (char *)malloc(100);
둘 다 100 바이트 공간을 할당받는다.
하지만 위의 것은 int형 값들을 읽고 쓰는 공간으로 만들기 위해 (int *)로 캐스팅한다.
#include <stdio.h>#include <float.h>intmain(void)
{
printf("float size is %d\n", sizeof(float));
printf("float min %e, max %e\n\n", FLT_MIN, FLT_MAX);
printf("double size is %d\n", sizeof(double));
printf("double min %e, max %e\n\n", DBL_MIN, DBL_MAX);
return0;
}
실행결과
최소와 최대값이 지수형으로 표시되었다.
float의 최소값을 예로 들면,
그리고, 최소값이 음수가 아닌 양수이다.
이것은 마이너스 부호만 붙이면 최대값이 최소값이 되기 때문이다.
따라서, 절대값으로 표현할 수 있는 가장 작은 수를 최소값으로 한 것이다.
double형의 최소, 최대값은 float형과
비교했을 때 어마무시하게 크고, 정밀하다는 것을 알 수 있다.
아래 동영상에서는
이진수 표현, 실수 데이터형인 float와 double, 그리고 overflow에 대해서 설명합니다.
#include <limits.>를 헤더파일에 포함시키고 (아래코드의 line 2 참조),
미리 지정된 값 (CHAR_MIN, CHAR_MAX, 등,, 아래 코드 확인)을
출력해 보면된다.
아래 프로그램은 위의 4가지 자료형의 크기, 최소, 최대값을
출력해주는 프로그램이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>#include <limits.h>intmain(void)
{
printf("char size is %d\n", sizeof(char));
printf("char min %d, max %d\n\n", CHAR_MIN, CHAR_MAX);
printf("short size is %d\n", sizeof(short));
printf("short min %d, max %d\n\n", SHRT_MIN, SHRT_MAX);
printf("int size is %d\n", sizeof(int));
printf("int min %d, max %d\n\n", INT_MIN, INT_MAX);
printf("long size is %d\n", sizeof(long));
printf("long min %d, max %d\n\n", LONG_MIN, LONG_MAX);
return0;
}