[아두이노] [강좌] 3. Blink 예제 해부하기(2) - 함수
이 전 강좌에서 변수에 대해 설명했고, 이번엔 함수에 대해 설명해보자.
수학에서 함수란, x에 어떤 값을 입력하면 수식을 거쳐 y라는 결과 값이 나오는 것을 뜻한다. “y=2x+1” 같은.
프로그래밍에서의 함수(Function)는 수학적 의미의 “Function(함수)”을 뜻한다고 생각해도 좋고, 일반적 의미의 “Function(기능)”을 뜻한다고 생각해도 좋다.
프로그래밍에서 함수는 어떠한 목적을 가진 여러 줄의 구문을 하나로 묶어둔 것이기 때문에 ‘기능’의 의미를 가지고 있지만, 그 기능이 동작하기 위해 x라는 입력 값이 존재할 수도 있고 y라는 결과 값이 존재할 수도 있으므로 ‘수학적 함수’의 의미 역시 가지고 있다고 볼 수 있기 때문.
.. "function"의 뜻을 물었을 때, 문과생들은 '기능'이라고 답하고 공대생들은 '함수'라고 답한다는 공대식 유머가 생각나네. ㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎ
함수는 다음 4가지를 반드시 가지고 있어야 한다.
1. 반환 값의 타입 2. 함수명 3. 매개 변수 4. 함수 구문 |
그리고 구조는 다음과 같다.
반환타입 함수명(매개변수) { 함수 구문 } |
“Blink” 예제의 setup() 함수를 살펴볼까? (파일→예제→01.Basics→Blink)
최신 버전의 스케치 툴에서 실행한 “Blink” 예제라면 맨 윗 줄(‘//’로 시작하는 주석문을 제외했을 때)의 “int led = 13;” 구문이 없을 것이다. 신경 안 써도 됨.
setup() 함수의 구조를 살펴보자. 빨간 색 네모 부분이 setup() 함수 부분이다.
참고로, “//”으로 시작하는 문장은 ‘주석문’으로 소스에 대한 설명이 주로 들어간다. 주석문은 프로그램 소스에 포함되지 않는다. “//”로 시작하여 다음 줄로 넘어가기 전까지의 구문이 모두 주석문으로 처리되며, 여러 줄의 주석문을 사용하고 싶을 때는 “/*”와 “*/”를 사용한다. “/*”부터 “*/”까지의 모든 구문이 주석문으로 처리된다, 줄바꿈에 상관 없이.
위에서 설명한 함수 구조와 setup() 함수 구조가 같은지 살펴보자. 주석문은 제외한다.
반환타입 함수명(매개변수) { 함수 구문 } |
void setup() { pinMode(led, OUTPUT); } |
음, 비슷해 보인다.
setup() 함수의 반환 타입은 “void”이다. 앞 강좌에서 ‘변수’의 종류에 대해 설명할 때 맨 앞에 “void”가 있었던 것이 기억날 것이다. 응? 기억 안나요?
“void”는 변수 타입이라기 보다는 ‘반환 타입이 없다’라는 것을 의미하는 키워드로 사용된다. 이는 함수에서 나오는 결과 y가 없다는 것을 의미한다. 없으면 안 써주면 되는 거 아니고? 아니다. 반환 값이 없으면 없다는 것을 나타내기 위해 “void”라고 반드시 명기해줘야 한다.
매개 변수의 경우에도 마찬가지다. 매개 변수란 함수가 자신의 기능을 수행하기 위해 받는 입력 값, 즉 x를 말하는데, 이 매개 변수는 개수에 제한이 없다. x가 여러 개일 수도 있다는 말. 그리고 반환 값처럼 없을 수도 있다는 말. 매개 변수가 없을 경우에 반환 타입의 경우와 마찬가지로 “void”라고 명시해줘야 하지만, 아두이노에서는 매개 변수에 한해서 “void”를 생략할 수 있다.
즉, “void setup()”은 사실 “void setup(void)”에서 “void”가 생략된 구문이라는 말이다. 아두이노의 경우에는 “void”를 생략할 수 있지만, 다른 프로그래밍 언어의 경우 매개 변수 자리의 “void”가 생략되는 것을 허용하지 않는 경우도 있으므로 유의할 것.
그리고 함수명으로 “setup”이라는 이름이 사용되었다. 함수의 기능을 쓰고자 할 때 이 함수명(매개 변수가 존재한다면 매개 변수 값도 함께)을 써주기만 하면 되며, 이 것을 “함수를 호출하다”라고 한다. setup() 함수는 아두이노 내부 소스에서 호출되므로 호출되는 부분을 볼 수는 없다.
마지막으로 함수 구문. 실제로 기능을 수행할 여러 수식 또는 또 다른 함수들을 뜻한다. 함수 구문은 반드시 “{“ 문자로 시작해서 “}” 문자로 끝나야 한다. Blink 예제의 setup() 함수의 기능은 “pinMode(led, OUTPUT)”이라는 다른 함수를 호출하는 것.
다시 한 번 자세히 살펴보자.
setup() 함수는 함수명이 “setup”이며, 반환되는 결과 값이 없으므로 “void”가 함수명 앞에 붙어있고, 매개 변수가 없음을 나타내는 “void”는 생략되어 있다. 그리고 이 함수는 “led”라는 변수와 “OUTPUT”이라는 값을 매개 변수로 하는 “pinMode”라는 함수를 호출하는 함수 구문으로 이루어져 있다.
그리고, 이렇게 함수의 구조를 만드는 것을 “함수를 정의하다”라고 한다.
어렵지 않지요?
함수를 정의하고, 호출하는 예제를 살펴보자. 반환 값이랑 매개 변수가 있는 걸로. 쉬운 걸로.
int addFunc(int a, int b) { int c; c = a+b; return c; }
void main() { int r=0; r = addFunc(1, 2); } |
쉬운가? 흠.
두 개의 함수가 정의되어 있다. “addFunc”라는 함수 하나와 “main”이라는 함수 하나. “main”이라는 함수는 아주 특별한 함수로, 프로그램이 시작되면 자동으로 호출되는 지정 함수이다. 아두이노에서는 main 함수가 내부 소스에 숨겨져 있고, 이 main 함수에서 setup() 함수와loop() 함수가 호출된다, 는 건 그냥 알고 지나가면 됨. 또 줄줄이 사탕이 붙을 뻔..-_-;;
“main”이라는 함수를 먼저 보면, 함수 안에 ‘지역 변수’로 “r”이라는 정수형 변수가 선언되어 0으로 초기화 되었고, 바로 다음 구문에서 “addFunc”라는 함수를 호출하여 “r” 변수에 저장하고 있다. “addFunc” 함수의 반환 값을 “r” 변수에 저장한다는 뜻.
그리고 “addFunc” 함수의 매개 변수로 숫자 두 개를 전달하고 있다. 그럼 “addFunc” 함수가 정의된 부분을 확인해 볼까? “int addFunc(int a, int b)”라고 반환 타입과 함수명, 그리고 매개 변수를 정의하고 있다. “int”형 반환 값을 가지며, “addFunc”라는 함수명을 사용하고, “int”형 변수 ‘a’와 ‘b’ 두 개를 매개 변수로 받는다는 뜻. 그래서 “addFunc” 함수를 호출할 때 1과 2를 매개 변수로 전달했고, 반환되는 값을 “int”형 변수 ‘r’에 저장한 것.
함수를 정의할 때 매개 변수를 정의하는 부분에서 매개 변수가 “void”가 아닐 경우, 매개 변수의 타입과 전달 받을 값을 변수명 뒤에 지정해줘야 한다는 것을 기억하자. 위 예제에서 “(int a, int b)”라고 정의한 것처럼.
“addFunc” 함수의 기능은 입력 값 ‘a’와 ‘b’를 더한 값을 “int”형 변수 ‘c’에 저장하고, ‘c’의 값을 반환하는 것이다. “main” 함수에서 “addFunc” 함수에 1과 2를 각각 매개 변수 ‘a와 ‘b’로 전달하였으므로, 반환되어 ‘r’ 변수에 저장되는 값은 3이 될 것이다.
함수를 호출할 때 유의해야 할 점은 매개 변수의 타입과 개수가 맞아야 한다는 것. 맞지 않을 경우 컴파일 에러가 발생한다. 반환 타입의 경우도 마찬가지.
그래서, 다시 Blink 예제로 돌아와서. setup() 함수는 설명했고, 이번엔 loop() 함수를 살펴보자.
void loop() { digitalWrite(led, HIGH); delay(1000); digitalWrite(led, LOW); delay(1000); } |
이젠 쉽지?
반환 값은 없음(void), 함수명은 “loop”, 매개 변수 없음(void 생략). loop() 함수의 기능은 "digitalWrite”라는 함수와 “delay”라는 함수를 2번 반복해서 호출하는 4 문장으로 이루어져 있다는 것.
하지만 loop() 함수는 아두이노 보드의 전원이 끝날 때까지 무한히 반복되는 함수, 위에서 배웠던 단어를 선택하여 말하자면 무한히 “호출”되는 함수이므로 Blink 예제에서는 loop() 함수의 4 문장이 무한히 반복되어 실행된다.
아두이노는 setup() 함수와 loop() 함수가 반드시 정의되어야 하며, 만일 둘 중 하나라도 정의되지 않는다면 컴파일 에러를 발생시켜 소스가 업로드 되지 않는다.
앞 강좌에서 간단하게 설명했었지만, setup() 함수는 아두이노의 전원이 들어온 후, 또는 리셋이 발생한 후 단 한번 호출되는 함수이고, loop() 함수는 setup() 함수가 호출된 후 아두이노의 전원이 끝날 때까지 무한히 반복되어 호출되는 함수라는 점, 기억해두자.
사실 바로 위의 5줄이 이번 강좌에서 말하려고 했던 핵심 주제인데, 함수 설명마저 이렇게 길어질 줄이야. ㅜㅜ
원래는 변수나 함수 설명은 다른 책이나 강좌를 찾아보라고 넘어가고, 아두이노에 대한 내용만 강좌에 넣을 예정이었다. 그런데 아두이노 카페나 나한테 들어오는 질문들을 보다 보면 예상외로 프로그램의 가장 기본인 변수와 함수의 개념을 정확하게 알지 못해서 원하는 동작을 만들지 못하는 경우가 많더라고. 그래서 한 번은 설명하고 넘어가야겠다, 싶었다.
물론 몰라도 된다. ‘아두이노’니까. 프로그래밍에 대해 전혀 모르는 사람도 만져 볼 수 있도록 하기 위해 만들어진 녀석이니까.
그래도, 한 번의 작품으로 끝날 ‘아두이노’가 아니라면. 아두이노를 이용해서 나 스스로 뭔가를 계속해서 만들고 싶고 또 이왕이면 잘 만들고 싶다면. 프로그래밍 언어에 대한 기초 지식은 반드시 필요한 것이라고 확신한다.
뭔가 진지해져서 오글거리니까, 오늘은 여기까지. 다음 강좌에선 setup() 과 loop() 함수에서 호출하고 있는 또 다른 함수들에 대해 알아보자. 안녕!