아두이노 C 프로그램에서 사용되는 주요 변수 개념을 정리하였습니다.정수형,실수형,문자와 문자열 변수에 대하여 알아봅시다.
아두이노 스케치를 구동하면 먼저 setup()함수가 한번 수행된 후 loop()함수는 무한 반복합니다.다음은 1초 동안에 loop()함수가 몇번이나 수행되는지 세어 보는 스케치입니다.
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 |
void setup() { // put your setup code here, to run once: Serial.begin(9600); } void loop() { // put your main code here, to run repeatedly: static unsigned long nextMil; static unsigned long loopCount = 0; static bool isFirstLoop = true; if (isFirstLoop) { isFirstLoop = false; nextMil = millis() + 1000; // 1초 후 } if (millis() > nextMil) { Serial.print("loopCount for a second: "); Serial.println(loopCount); while(1) { } } loopCount++; } |
결과는 다음과 같습니다.
1초 동안에 24만번 이상 수행하는군요.생각보다 많은 수치가 아닌가요?
스케치를 만들 때는 이런 상황를 감안해야 합니다.차칫 잘못하면 아두이노에 달려 있는 센서를 필요 이상으로 혹사시킬 수도 있습니다.
1 2 3 |
static unsigned long nextMil; static unsigned long loopCount = 0; static bool isFirstLoop = true; |
함수 안에서 선언되는 함수를 지역 변수(local variable)이라고 하는데,한 번 수행되면 값을 잃어 버립니다.그래서 계속 값을 유지하고 싶을 때는 앞에 static을 붙여 주면 됩니다.지역 변수는 자동으로 초기화되지 않으므로 주의하여야 합니다.static으로 선언된 변수의 초기화는 첫 수행시에 한 번만 이루어집니다.
아주 큰 수를 다루고 싶을 때는 변수형을 unsigned long으로 선언하면 되는데 대략 42억정도까지 표현할 수 있습니다.
bool(lean)형의 변수는 참(1,true)이나 거짓(0,false) 두 가지 상태만을 표현하게 됩니다.
1 |
nextMil = millis() + 1000; // 1초 후 |
millis()함수는 1/1000초 단위로 시간을 알려주는데 그 값은 1초에 1000씩 증가합니다.이 경우는에는 큰 수를 담을 수 있도록 unsigned long으로 선언된 변수를 사용하면 됩니다.unsigned long으로 선언된 변수는 약49일치의 millis()까지만 보관할 수 있습니다.
1 2 |
while(1) { } |
이 문장은 특정 지점에서 흐름을 멈추고 그 자리에서 무한 반복할 때 사용합니다.위의 경우에는 중간에 빠져나올 수 있는 조건식이 없으므로 스케치를 중단하려면 리셋하거나 전원을 차단해야 합니다.
아래는 위 스케치와 동일한 기능을 하는 새로운 스케치와 그 수행 결과입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
//char resultMsg[] = "loopCount for a second: "; char *resultMsg = "loopCount for a second: "; unsigned long nextMil; unsigned long loopCount; void setup() { // put your setup code here, to run once: Serial.begin(9600); nextMil = millis() + 1000; // 1초 후 } void loop() { if (millis() > nextMil) { Serial.print(resultMsg); Serial.println(loopCount); while(1) { } } loopCount++; } |
스케치가 이전 것 보다 간략해져서 수행 수치가 조금 높아졌군요.
1 2 3 4 |
//char resultMsg[] = "loopCount for a second: "; char *resultMsg = "loopCount for a second: "; unsigned long nextMil; unsigned long loopCount; |
문자열 포인트형인 “char *”는 문자열을 표현할 수 있는 또 하나의 새로운 방법입니다.이렇게 선언된 변수는 문자열이 보관된 주소를 보관하고 있습니다.문자열을 다루는 문자 배열과 비슷한 방식으로 사용하면 됩니다.이 부분은 다음에 다시 설명드리도록 하겠습니다.
함수 바깥에서 선언하면 전역 변수(global variable)가 되어 모든 함수에서 접근할 수 있습니다.수치형의 전역 변수는 별도로 지정하자 않아도 0으로 자동 초기화 됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
int thisVal; int prevVal; void setup() { // put your setup code here, to run once: Serial.begin(9600); for (unsigned long i = 0; i <100000; i++) { if (thisVal < prevVal) { Serial.print("overflow(int) at "); Serial.print(i); Serial.print(" this:"); Serial.print(thisVal); Serial.print(" <--- prev:"); Serial.println(prevVal); } prevVal = thisVal; thisVal++; } } void loop() { // put your main code here, to run repeatedly: } |
int형 변수는 가장 큰 수가 32767이고 32768번째에는 -32768로 바뀐다.다시 1씩 증가시키면 0과 32767을 거쳐 다시 -32768로 바뀌는 과정을 반복합니다.즉 int형의 변수는 16bit로 이루어져 있으므로 그 크기는 -2의(16-1)승에서 +2의(16-1)승-1까지입니다.
다음 스케치는 unsigned int형 변수의 경계 넘침을 나타내고 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
unsigned int thisVal; unsigned int prevVal; void setup() { // put your setup code here, to run once: Serial.begin(9600); for (unsigned long i = 0; i <100000; i++) { if (thisVal < prevVal) { Serial.print("overflow(int) at "); Serial.print(i); Serial.print(" this:"); Serial.print(thisVal); Serial.print(" <--- prev:"); Serial.println(prevVal); } prevVal = thisVal; thisVal++; } } void loop() { // put your main code here, to run repeatedly: } |
unsigned int형은 음수는 표시하지 않고 0에서 65535까지 나타내고 있습니다. 즉 0에서 2의(16-1)승까지 표현할 수 있습니다.
다음은 정수형인 변수의 크기를 나타낸 표입니다.
크기 | 선언 | 최소값 | 최대값 |
---|---|---|---|
1바이트 (8비트) | char | -128 | 127 |
unsigned char =byte | 0 | 255 |
|
2바이트 (16비트) | int | -32768 | 32767 |
unsigned int | 0 | 65535 |
|
4바이트 (32비트) | long | 약-21억 | 약21억 |
unsigned long | 0 | 약42억 |
대체로 배열의 인덱스나 반복을 나타낼 때는 int형을 사용하고 millis()와 같은 큰 수를 표현할 때는 unsigned long을 사용하면 무리가 없습니다.메모리 크기가 중요한 때는 범위에 최적화된 경제적인 변수형을 선택하면 됩니다.
char형은 본질적으로는 8bit 정수형 숫자이지만 Serial.print(ln)문에서는 ASCII표에 대응하는 문자로 표시해줍니다.
다음은 10.0에 0.1씩 누적하여 더하는 스케치입니다..
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
float f1 = 10.0; float f2; void setup() { // put your setup code here, to run once: Serial.begin(9600); for ( int i = 0; i < 10; i++ ) { float f2 = f1 + 0.1 * i; Serial.println(f2,10); } } void loop() { // put your main code here, to run repeatedly: } |
float형 변수는 표현 방법이 특수해서 10진수인 숫자를 꼭 맞게 표현하지 못할 수 있으므로 주의하여야 합니다..
실수형 변수의 특징입니다.
- 숫자의 음양,유효숫자,지수 부분을 나누어서 보관합니다.
- 상상할 수 있는 대부분의 큰 수를 표현할 수 있습니다.
- 4바이트를 사용합니다.
- 유효 숫자가 6~7자리 정도이므로 정밀하게 숫자를 표현하기 어려워서 큰 숫자를 세는데 적합하지 않습니다.
- 일반적으로 double형은 float형보다 더 정밀하게 표현할 수 있지만 아두이노에서는 double형과 float형을 같이 취급합니다.
- 여러 단계를 거쳐서 처리되므로 수행에 소요되는 시간이 상대적으로 깁니다.
따라서 실수형 변수는 다음과 같이 사용하면 좋습니다.
- 소숫점이 있는 경우는 선택의 여지없이 사용합니다.
- 10진수를 딱 떨어지게 표현하지 못하는 경우가 많으므로 조건문에서 비교할 때 “==” 보다는 “>”나 “<“를 포함하는 게 좋습니다.
- 정수형으로 변환하여 처리하거나 보관하면 더 효율적인 경우도 있습니다.
- char 변수[] : char의 배열로 표시
- char * : char의 포인터로 표시
- String 클래스 이용
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
char c = 'A'; //①문자 배열 항목수는 생략하고 문자열 상수로 초기화 char csA[] = "hellow"; //②문자 배열 항목수는 기입하고 문자열 상수로 초기화 char csB[7] = "hellow"; //③문자 배열 항목수는 기입하고 문자 배열로 초기화 char csC[7] = {'h','e','l','l','o','w','\0'}; //④문자 배열 항목수는 기입하고 문자 배열로 초기화,뒤에 남는 배열 공간 있음 char csD[20] = {'h','e','l','l','o','w','\0'}; //⑤문자 포인터에 문자열 상수 넣기 char *cpA = "hellow"; //⑥String 클래스에 문자열 상수 넣기 String stA = "hellow"; void setup() { // put your setup code here, to run once: Serial.begin(9600); // 문자 표시 printVariousString(); // 문자 배열에 문자열 상수 넣기 strcpy(csD,"myNameIsHong"); Serial.print("strcpy(csD,\"myNameIsHong\"); //csD ["); Serial.print(csD); Serial.println("]"); // 문자 배열에 문자열 상수 덧붙이기 strcat(csD,"Gildong"); Serial.print("strcat(csD,\"Gildong\"); //csD ["); Serial.print(csD); Serial.println("]"); // String 클래스에 문자열 상수 넣기 stA = "myNameIsHong"; Serial.print("stA = \"myNameIsHong\"; //stA ["); Serial.print(stA); Serial.println("]"); // String 클래스에 문자열 상수 덧붙이기 stA += "Gildong"; Serial.print("stA += \"Gildong\"; //stA ["); Serial.print(stA); Serial.println("]"); } void loop() { // put your main code here, to run repeatedly: } void printVariousString() { Serial.print("c\t["); Serial.print(c); Serial.print("]\ncsA\t["); Serial.print(csA); Serial.print("]\ncsB\t["); Serial.print(csB); Serial.print("]\ncsC\t["); Serial.print(csC); Serial.print("]\ncsD\t["); Serial.print(csD); Serial.print("]\ncpA\t["); Serial.print(cpA); Serial.print("]\nstA\t["); Serial.print(stA); Serial.print("]\n"); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
char c = 'A'; //①문자 배열 항목수는 생략하고 문자열 상수로 초기화 char csA[] = "hellow"; //②문자 배열 항목수는 기입하고 문자열 상수로 초기화 char csB[7] = "hellow"; //③문자 배열 항목수는 기입하고 문자 배열로 초기화 char csC[7] = {'h','e','l','l','o','w','\0'}; //④문자 배열 항목수는 기입하고 문자 배열로 초기화,뒤에 남는 배열 공간 있음 char csD[20] = {'h','e','l','l','o','w','\0'}; |
문자열을 충분히 담을 수 있을 만한 크기로 char형 문자 배열을 선언하고 배열 첫 항목부터 한 글자씩 채워가면 됩니다.문자열의 길이를 별도로 보관하는 장소가 없기 때문에 배열에 보관된 마지막 문자 뒤에 0을 채워서 그 끝으로 삼기로 하였습니다.0으로 마감을 하면 그 뒤 배열에 속한 내용은 무시합니다.
따라서 char형 배열의 크기를 정할 때는 담을 문자열 크기 보다 하나 더 큰 값이상으로 해야합니다.
문자열을 Serial.print할 때는 시작 주소를 지정하면 됩니다.문자열 배열명은 실제는 배열 첫 항목의 주소이므로 Serial.print에 지정하면 배열에 속한 문자열이 출력됩니다.
다음은 스케치를 살펴보고 문자열 배열의 이용 방법을 알아봅시다.
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 32 33 34 35 36 |
char csA[6] = "hello"; void setup() { // put your setup code here, to run once: Serial.begin(9600); // 문자 배열 표시 Serial.print("Serial.print(csA); // ["); Serial.print(csA); Serial.println("]"); // 두번째 항목 표시 Serial.print("Serial.print(csA[1]); // ["); Serial.print(csA[1]); Serial.println("]"); // 두번째 항목부터 문자열 표시 Serial.print("Serial.print(&csA[1]); // ["); Serial.print(&csA[1]); Serial.println("]"); // 배열명 번지 계산으로 두번째 항목 표시 Serial.print("Serial.print(*(csA+1)); // ["); Serial.print(*(csA+1)); Serial.println("]"); // 배열명 번지 계산으로 두번째 항목부터 문자열 표시 Serial.print("Serial.print(csA+1); // ["); Serial.print(csA+1); Serial.println("]"); } void loop() { // put your main code here, to run repeatedly: } |
변수의 주소를 구할 때는 변수 앞에 &를 붙이면 됩니다.또한 특정 주소의 내용을 지정할 때는 *를 붙이면 됩니다.
C에서 포인트는 많이 사용되지만 개념을 이해하기가 쉽지 않습니다.지금 당장 이해가 어려워도 나중에 자연스레 알게 됩니다.
1 |
char *cpA = "hello"; |
“char *”는 문자형 포인트 변수를 정의합니다.문자열을 표현할 때 사용하는데,문자 배열명을 사용할 때와 거의 같은 방식으로 사용하면 됩니다.
Serial.print(ln)문에 문자형 주소를 지정하면 문자열을 출력하게 됩니다.
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 |
char *cpA = "hello"; void setup() { // put your setup code here, to run once: Serial.begin(9600); // 문자 배열 표시 Serial.print("Serial.print(cpA); // ["); Serial.print(cpA); Serial.println("]"); // 두번째 항목 표시 Serial.print("Serial.print(*(cpA+1)); // ["); Serial.print(*(cpA+1)); Serial.println("]"); // 두번째 항목부터 문자열 표시 Serial.print("Serial.print(cpA+1); // ["); Serial.print(cpA+1); Serial.println("]"); } void loop() { // put your main code here, to run repeatedly: } |
문자열 배열보다는 훨씬 쉽게 접근할 수 있습니다.
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 |
//⑥String 클래스에 문자열 상수 넣기 String stA = "hellow"; void setup() { // put your setup code here, to run once: Serial.begin(9600); // 문자열표시 Serial.print("Serial.print(stA); \t["); Serial.print(stA); Serial.print("]\n"); // String 클래스에 문자열 상수 넣기 stA = "myNameIsHong"; Serial.print("stA = \"myNameIsHong\"; //stA ["); Serial.print(stA); Serial.println("]"); // String 클래스에 문자열 상수 덧붙이기 stA += "Gildong"; Serial.print("stA += \"Gildong\"; //stA ["); Serial.print(stA); Serial.println("]"); } void loop() { // put your main code here, to run repeatedly: } |
댓글 남기기