아두이노만 바라봐선 절대 사물인터넷 제품 만들 수 없다

규링은 대학원 들어오기 전에 학부연구생을 했었다. 그때에는 사물인터넷이 엄청나게 주목받을 때였고, 다들 아두이노 배우기 시작하고 그전에 조금이라도 전자회로 공부하고 했던 사람들은 여기저기서 강의를 뛰고 했다. 그리고 그걸 빌미삼아서 메이커 프로젝트도 많이 발전하고 했다만…

그때부터 나랑 우리 교수님은 사물인터넷으로 연구하는 걸 포기했다.

발전하는 방향성이 어이가 없었다. 여러모로…

그 중에서도 제일 공감하던 것이 있다면 바로 아두이노를 가지고 제품화를 밀어부치려는 사람들 때문이었다.

아두이노를 통해서 프로토타입을 만들 수는 있다. 그건 확실하다. 근데 그 이상으로 제품화를 하기 시작하면 아두이노를 이용해야 하는 것이 아니라 그 안에 있는 atmel을 다룰 수 있어야 한다. 아두이노의 부트로더와 코드를 그대로 인풋해서 이용한다 하더라고 회로 최적화를 할 수 있어야 한다. 그에 걸맞는 제품 디자인도 같이 진행할 수 있어야 한다. 그 외에도 제품화를 진행하기 위해서는 많은 작업들이 더 필요하다. 근데 문제는 그 밑에 작업들을 하기에는 너무 어렵고, 아두이노를 가지고 쉽게 한 것에는 한계가 있는 거 같고 해서 더 이상 진행하지도 않고 끝내는 경우가 상당히 많다. 그래서 제대로 된 제품화가 이루어지는 곳이 별로 없다. (돈없어서 그러는 곳 말고)

그러나 지금까지도 이어져 오고 있는 아두이노에 대한 것은 바뀌지 않았다. 한국만 그런 거 같은 기분이 진짜 많이 든다. 뭐, 지금 한국의 사물인터넷은 대기업.. 이라기보단 통신사들만이 할 수 있는 특수한 것이라는 그런 특성적 이미지가 많이 깔려서 그런지 자꾸 뭔가 통신 잘 하는 거 외엔 신경도 안쓰는 그런 분위기이다.

물론 프로토타입을 제대로 만든 다음에 제대로 제품화까지 성공한 기업들도 있다. 스타트업에 보면 그런 기업들 있다. 많지는 않은 거 같지만…(소식 들리는 게 얼마 없어서 그런걸까?) 그런데, 그런 기업들이 하나 둘 있다고 해서 그렇게 하면 되겠지라고 하는 것도 또한 아니다. 지향하는 것이 다 다르기 때문에 그에 따라 만들 수 있는 길도 다 다르다. 그걸 간과하지 못하는 이상 제품화 하는 데 있어서는 더 이상 나가려고 하지 않을 것이다.

그럼 뭐하냐고? 아두이노 교육으로 돈 벌어먹고 있을 확률이 제일 높다.

23 – 연산자(대입)

대입 연사자는 변수의 값을 변경하기 위해 이용한다. 그전까지 많이 써먹었던 a=5 같은 것이 대입 연산자이다. 기본적인 대입연산자인 =은 오른쪽에 있는 값을 왼쪽으로 대입한다.

대입 연산자에 쓰이는 것은 바로 ‘값’이다. a=5라고 한다면 ‘a라는 변수의 값에다가 5라는 값을 넣어라’라는 뜻이 되고, a=b라고 한다면 ‘b 변수에 있는 값을 a 변수의 값으로 바꿔넣어라’라는 것이 된다.

이 외에도 복합 연산자라고 해서 둘 이상의 기능을 합친 연산자들이다. 제일 흔하게 보는 것이 +=인데 다음과 같이 작성한다.

a += 3
은 아래와 같다
a = a + 3

이런 대입 연산자들은 다음과 같이 있다.

  • 연산자 | 의미
  • = | 우변의 값을 좌변에 대입
  • += | 좌변에 우변 값을 더한 결과를 좌변에 대입
  • -= | 좌변에서 우변 값을 뺀 결과를 좌변에 대입
  • *= | 좌변에 우변 값을 곱함 결과를 좌변에 대입
  • /= | 좌변에 우변 값을 나눈 결과를 좌변에 대입
  • %= | 좌변에 우변 값을 나눈 나머지를 좌변에 대입
  • >>= | 좌변 값을 우변 값만큼 오른쪽으로 시프트한 결과를 좌변에 대임
  • <<= | 좌변 값을 우변 값만큼 왼쪽으로 시프트한 결과를 좌변에 대입
  • &= | 좌변과 우변을 and 연산한 결과를 좌변에 대입
  • |= | 좌변과 우변을 or 연산한 결과를 좌변에 대입
  • ^= | 좌변과 우변을 xor 연산한 결과를 좌변에 대입

실제 예시를 짜봤다.

%e1%84%89%e1%85%b3%e1%84%8f%e1%85%b3%e1%84%85%e1%85%b5%e1%86%ab%e1%84%89%e1%85%a3%e1%86%ba-2017-02-16-%e1%84%8b%e1%85%a9%e1%84%92%e1%85%ae-2-58-06

22 – 연산자(관계)

관계 연산자는 좌변과 우변의 값을 비교해서 그 결과를 참(True, 1)과 거짓(False, 0)으로 판명한다.

  • 연산자 / 의미 / 예시
  • == / 같다 / a == b
  • != / 다르다 / a != b
  • < / 작다 / a < b
  • <= / 작거나 같다 / a <= b
  • > / 크다 / a > b
  • >= / 크거나 같다 / a >= b

관계 연산자의 결과값을 받아서 보여주는 예시를 참고한다. 변수에 참과 거짓값을 확인하는 것으로 구성되었다.

(보충) 최신 gcc에서의 선언문 차이

블로그에 작성중인 c의 경우에는 지금 최신의 주분투와 최신 버전의 gcc를 이용하고 있다. 그러나 작성하는 내용은 이전에 규링이 직접 c를 배울 때 쓰던 책 자료도 참고로 하고 있다. 그러다 보면 요즘과는 차이가 좀 나는 부분이 있는데, 이번에 자료를 만들면서 겪었다.

변수 선언을 할 때, 한번에 특정 변수를 동일한 값으로 취급하여 선언하는 경우가 있다. 이전에 작성한 산술 연산자 글에서의 두번째 예시에 보면 a,b,c,d 네 변수를 차례차례 선언한 수에 c=d=2를 작성하여 c,d를 한번에 2로 지정하였다.

근데 c를 오래전부터 짜왔던 분들은 그냥 선언할 때 저 두번째 문장을 확 넣어버리도록 배웠다. 그리고 그 때에는 그대로 했어도 오류가 나지 않았다. 그게 아래에 있는 예시이다.

근데 지금은 이렇게 하면 오류를 낸다.

오류 문장을 보면 d가 선언되지 않았다고 한다. 이게 문제될 것이 뭐냐면서 하는 분들이 있는데, 요즘에는 컴파일러들이 이걸 문제삼는다. 선언을 먼저 함으로써 메모리 할당을 보호하려는 정책인 듯 하다. 자세한 건 찾아봐야 할텐데…;;

21 – 연산자 (산술)

산술 연산자는 수치 계산에 이용되는 연산자로 덧셈, 뺄셈, 곱셈, 나눗셈을 수행한다. 그리고 정수에 대한 나눗셈 연산에서는 소수점 이하는 버린다. 나머지 연산에는 %를 이용하여 나머지만을 나타내며, 정수 연산만 가능하다.

연산자 / 의미 / 예시

  • + / 두 연산자를 더함 / a + b
  • – / 앞의 연산자에서 뒤의 피연산자를 뺌 / a – b
  • * / 두 피연산자를 곱함 / a * b
  • / / 앞의 피연산자에서 뒤의 피연산자를 나눔 / a / b
  • % / 앞의 피연산자에서 뒤의 피연산자를 나눈 나머지 a % b
  • ++ / 피연산자에 1 증가 / a++
  • — / 피연산자에 1 감소 / a–

예시로 +, *를 이용한 예시를 보여준다.

증가 연산자의 경우에는 프로그래밍을 할 때 상당히 많이 이용한다. 사용할 때 조심해야 하는 것이 있는데, ++의 위치이다. a++은 a를 사용한 후에 1을 증가하고, ++a는 a를 사용하기 전에 1을 증가시킨다. 이 둘의 차이를 보여주기 위해서 동일한 식을 보여주자면 아래와 같다.

a = b++;
의 경우에는
a = b;
b = b + 1;
와 같은 식이다.

a = ++b;
의 경우에는
b = b + 1;
a = b;
와 같은 식이다.

이 둘의 차이를 확인하기 위한 예시가 아래의 예시이다. 하나 하나 천천히 실행해서 확인해 본다.

20 – 함수

이 글은 글의 내용이 한번에 쭉 이어져서 글이 좀 길다. 그래서 예시 코드와 실행 결과를 슬라이드 쇼로 만들어서 삽입하였으니 읽을 때 양해를 부탁한다.

프로그램의 시작은 main이라는 함수에서 시작을 한다고 했다. 그러나, 큰 프로그램을 만드는 데 있어서 main 함수 하나로만 작성하면 작성하기도 어려울 뿐더러 일일이 나열하면 반복적으로 작성해야 하는 것들도 많이 생기는데, 이를 일일이 작성할 수는 없다. 이를 위해 작업을 처리하는 단위인 함수로 나눠서 작성하면 소스코드의 길이가 짧아질 뿐만 아니라 다른 프로그램에서도 사용할 수 있도록 만들 수 있다. 함수를 사용하는 예제를 아래와 같이 확인한다.

C언어에서는 main 함수 내에 있는 문장을 순차적으로 실행하는데, 예시 소스의 11번째 라인에 있는 func(); 는 func 함수를 실행하시오라는 의미이다. 그래서 func 함수를 실행을 하고, 함수 실행이 다 끝아면 다시 main 함수의 다음 줄인 12번 줄을 실행한다.

함수 또한 정의를 해야 main 함수에서 알 수 있다. 그래서 main 함수 위에 함수를 작성한 것이 바로 함수를 정의한 것이 된다. 만약 main 함수 밑에 함수를 정의하면 오류를 발생시킬 것이다. 아래의 예시는 직접 오류를 발생시켰다.

이런 경우에는 함수를 별도의 변수처럼 한 줄로 선언함으로써 해결할 수 있다. 함수 선언은 함수 호출 전에 어떤 함수가 정의되어 있는지 먼저 알려주는 것으로, 일반적으로 프로그램 시작 부분에 나타낸다. 선언이 위에 있으면, 함수를 직접 정의하는 부분이 호출하는 함수보다 아래에 있어도 상관이 없다. 지금 학습하는 데 있어서 호출하는 함수는 main 함수이므로, main 함수보다 아래에 함수를 정의하려면 main 함수 위에 함수 선언을 따로 해줘야 한다.

함수를 정의하는 작업을 그냥 위에 하면 되지 않느냐고 묻는 사람들이 많은데, 함수의 수가 많거나 함수간에 호출의 경우에는 어떤 것을 먼저 정의하고 나중에 정의하느냐에 따라서도 구성을 생각해야 하는 경우가 생긴다. 함수가 함수를 부르는 경우에는 특히 더더욱 심각한 문제가 된다. 그래서 미리 헤더를 삽입하는 곳 아래에 함수들을 미리 선언하고 사용한다. 그러면 별도로 이해력이 떨어지는 코드를 짜지 않아도 된다.

함수를 선언하는 예시는 아래와 같이 구성하면 된다.

프로그램은 대부분 여러 개의 함수를 사용한다. 그래서 함수를 하나 더 추가해보도록 한다.

지금까지 작성한 함수는 기존의 main 함수와는 다른 걸 볼 수 있다. 그렇다. return을 마지막에 작성해 주지 않았다. 함수를 실행하고 나면 해당되는 반환값이 없는 것이다. 그래서 void형을 앞에 적어줬다. 그리고 함수를 호출할 때, 어떤 값을 전달해 주지도 않았다. 함수는 반환값과 전달하는 매개변수에 따라서 다음과 같이 분류할 수 있다.

반환하는 값과 매개변수가 없는 함수

반환하는 값은 있고 매개변수는 없는 함수

반환하는 값은 없고 배개변수는 있는 함수

반환하는 값과 매개변수가 있는 함수

우리가 주로 작성하던 main 함수를 보면 반환값에 대한 것은 쉽게 알 수 있다. 앞에 int 라고 작성한 것은 int형 변수를 반환한다는 것이고, 마지막 줄에 있는 return값은 반환할 값이 이것이란 것이다. 그럼 일단 반환값이 있는 함수는 어떤 형식인지 예시로 확인하겠다. 아래 예시에는 return에 5+7이라고 되어 있는데, 5+7을 한 결과값을 반환하는 것이다. 그래서 반환된 값을 main에 있는 i 변수가 받아서 출력을 하니 12가 나온 것이다.

반환값이 없고 매개변수가 있는 함수는 어떤 구조인지 확인하겠다. 매개변수는 함수의 () 안에 넣으면 받아지며, 함수를 선언하고 정의할 때에도 어떤 타입의 값을 받아올 것인지를 정의해줘야 한다.

위의 예제를 보면 함수 정의하고 선언하는 부분에 보면 func(int n1, int n2) 라고 되어 있는데, () 안에 있는 저 두 변수가 매개변수가 된다. 이런 매개변수를 통해 데이터를 주고받을 수 있는데, 특히 호출하는 쪽의 매개변수를 ‘실매개변수’, 함수를 정의하는 쪽의 매개변수를 ‘형식매개변수’라고 한다. 형식매개변수의 경우에는 어떤 형식이라고만 정의해도 되고 그 변수명에 대해서는 정의를 하지 않아도 되는데, 알아보기 쉽게 하기 위해서 일부러 다 정의하는 경우이다.

이젠 매개변수 및 반환값이 전부 존재하는 경우를 예시로 보도록 한다.

마지막으로 매개변수를 이용하지 않을 때를 보도록 한다. ()안에 아무것도 정의하지 않아도 되고, void라고 적어도 된다. 그 예제는 아래에 있다.

함수에 대해서 간단하게 알아보았다. 이를 통해 C언어의 기본적인 내용을 살펴봤다. 이젠 연산과 제어를 위한 작업을 정리할 것이다.

19 – 값을 변경할 수 없는 변수

변수인데도 불구하고 값을 변경할 수 없는 변수가 있다. 선언할 때에만 값을 지정하고 그 이후에는 값을 바꾸지 못하게 하는데, const를 선언할 때 앞에 붙여주면 된다.

const int a = 10;

이렇게 하면 a값은 10으로 고정되어 바꿀 수 없다. 일부러 바꾸려고 하면 오류가 날 것이다. 컴파일러가 오류를 내기 때문에 오류를 직접 예시로 보여주기로 한다.

18 – 데이터형의 이름 바꾸기

데이터 형의 이름이 너무 긴 경우이거나 특정하게 지정하고 싶을 경우에는 typedef를 사용해 데이터형의 이름을 별도로 만들 수 있다. 구조는 다음과 같다.

typedef [기존 데이터형] [새로 지정할 데이터형 이름];

예를 들어, unsigned int를 줄여서 u_int라고 만들고 싶으면 다음과 같이 만든다.

typedef unsigned int u_int;

그리고 실제 사용은 u_int를 이용하여 똑같이 선언한다.;

u_int data = 10;

typedef의 경우에는 주로 함수 바깥에 선언을 하여 사용을 한다. 또한 구조체와 같은 복잡한 자료구조를 다루고 할 때 많이 쓰이고 해서 구조체 설명할 때 좀 더 내용이 자세히 들어갈 것이다만, 실제로 사용하는 모습이 어떤지는 기본 구조를 예시로 작성하였다.

17 – 변수 (문자열형)

문자를 나열한 문자열을 저장하는 변수로, char형을 배열(연속된 저장공간. 배열에 대해서는 나중에 천천히 보도록 하겠음.)을 이용하여 문자열형을 저장한다. 배열에 대해서는 아래와 같이 여러개의 문자 변수가 연속으로 있는 상황이다. 여기에 “abc”를 저장하면, 아래와 같이 저장된다.

각각의 변수는 str[0] ~ str[3] 이런 순으로 들어있다. 이것까지 확인할 수 있는 예시를 만들어봤다.

분자열 변수도 선언과 동시에 초기화할 수 있다. 같은 예시를 동시에 초기화하는 것을 보여주는 예시를 다음과 같이 만들어봤다.

위에 있던 예제에서는 각 문자를 변수에 일일이 저장하였었다. 근데 수많은 문자열을 저렇게 일일이 할 수는 없기 때문에 문자열을 별도로 다루는 함수들이 존대한다. strcpy 함수를 이용하면 문자열을 배열에 한번에 저장할 수 있는데, strcpy(s, t) 함수는 문자열 t를 s에 복사하는 함수이다. string.h 헤더 파일에 정의되어 있기 때문에 헤더를 하나 더 추가해준다. 역시 아래 예시를 보면 알 수 있다.

문자열을 다룰 수 있는 함수들은 여러모로 있기 때문에 별도로 문서를 다뤄야 한다.

16 – 변수 (문자형)

하나의 문자를 저장하기 위한 데이터형으로 메모리 크기가 1바이트인 char형 변수를 이용한다.

상수 편에서도 이야기하였듯 모든 문자에는 대응되는 정수값이 있다. 즉, char형 변수에 저장할 수 있는 것은 문자 그대로뿐만 아니라 문자에 대응하는 숫자 또한 저장될 수 있다는 것이다. 이것을 같이 확인하기 위해서 예시를 작성하였다.