最近開始動手重寫 ROSA (Robot Operating System for Arduino),原本想利用 Arduino 的字串函數來處理字串,發現它的函數庫尚未優化,會佔用很多記憶體,因此留文記錄問題,並提供 ROSA 的解決方案。
正式討論 ROSA 程式之前,先從資料結構說起,話說 String 是一串字元以零值結尾,可以表示成
String str1 = "123456789"; 它也可以是字元陣列, char str2[10] = "123456789"; 或 char str3[10] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', 0}; 或 char str4[10] = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x00}; 或 char str5[10] = {49, 50, 51, 52, 53, 54, 55, 56, 57, 0}; 甚至可以是字元指標, char* str6 = str2; 將 str6 指標指向 str2 字串的起頭。
了解上述概念後,我們寫幾個程式測試,你會更加清楚怎麼一回事。
void setup() {
// put your setup code here, to run once:
Serial.begin(57600);
String str1 = "123456789";
Serial.println(str1);
}
void loop() {
// put your main code here, to run repeatedly:
}
這個程式編譯後,程式碼佔用 ROM 2642 bytes,RAM 使用 208 bytes後面的程式僅更改 setup() 比較差異,
void setup() {
// put your setup code here, to run once:
Serial.begin(57600);
char str2[10] = "123456789"; // ROM 1546 bytes, RAM 198 bytes
Serial.println(str2);
}
當陣列改成 {'1', '2', '3', '4', '5', '6', '7', '8', '9', 0}、{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x00} 或 {49, 50, 51, 52, 53, 54, 55, 56, 57, 0},都和 "123456789" 一樣。void setup() { // put your setup code here, to run once: Serial.begin(57600); char str2[10] = "123456789"; // ROM 1546 bytes, RAM 198 bytes char* str6 = str2; Serial.println(str6); }使用指標也相同於陣列。
=====================================================
再來測試字串與數值混雜顯示的情形,
void setup() { // put your setup code here, to run once: Serial.begin(57600); int a = 255; char str2[4] = "ABCD"; // ROM 1576 bytes, RAM 196 bytes Serial.print(str2); Serial.print(a); Serial.println("EF"); } void setup() { // put your setup code here, to run once: Serial.begin(57600); int a = 255; Serial.println(String("ABCD")+a+"EF"); // ROM 3132 bytes, RAM 206 bytes } void setup() { // put your setup code here, to run once: Serial.begin(57600); int a = 255; char str2[10]; // ROM 3020 bytes, RAM 196 bytes sprintf(str2, "ABCD%dEF", a); Serial.println(str2); } void setup() { // put your setup code here, to run once: Serial.begin(57600); int a = 255; Serial.println(STRING("ABCD%dEF", a)); // ROM 1728 bytes, RAM 206 bytes } void setup() { // put your setup code here, to run once: Serial.begin(57600); int a = 255; Serial.print(STRING("ABCD%dEF\n", a)); // ROM 1710 bytes, RAM 204 bytes }這個 STRING() 副程式是自己設計的,雖然無法像第一個例子 ROM size 那麼小,但是可以像 sprintf() 進行格式設定。
綜合上述比較,設計 STRING() 時,放棄使用 String 資料格式,改用 char 陣列或指標,另外使用 print() 不用 println(),現在僅提供三種整數格式轉換 %d, %nd, %0nd n = 1~9。
#ifndef STRING_LENGTH #define STRING_LENGTH 20 #endif
char* STRING(char* fmt, int t) { char s[STRING_LENGTH]; // 原先缺少 static 會讓函數無法正常多次執行 // 但現在有 static 會讓超音波偵測無法正常運作 char n[9] = " "; char a; char* si = s; byte f = 0; // 最後字串的起始位置 byte m = 0; // 0 %d 1~9 %nd or %0nd while (bool (a = *(fmt+(f++)))) { if (a == '%') { goto found; } *(si++) = a; // 暫存 % 之前字元 } goto end; found: while ((a = *(fmt+(f++))) != 'd') { m = a - '0'; if (m == 0) { for (a = 8 ; a>=0 ; a--) n[a] = '0'; } else if (m > 9) goto end; // 無效轉換 } if (t < 0) { *(si++) = '-'; // 負值 if (m != 0) m--; t = -t; } a = 9; do { n[--a] = '0' + (t % 10); // 轉換 t /= 10; } while (t != 0); if (a > 9-m) a = 9-m; for ( ; a < 9 ; a++) *(si++) = n[a]; // 插入數字 fmt += f; while (*(si++) = *(fmt++)) // 補 d 之後字串 {} *si = 0; fmt = s; end: return fmt; }
沒有留言:
張貼留言