2014-01-16 48 views
1

我一直有一個問題與字符指針,字符串,字符串和大多數事情指針相關的概念。也許我太老了這個;-)strcpy似乎與之前的串行輸出Arduino

全局聲明:

char * message;

serialOut是一個很短的8字符串,標識符(X10D),然後將數據(nnn)和空終止符。我發現通過串行發送的數據在前面被修剪,錯過了標識符。在第一次通過時,它將是完整和正確的,但在隨後的通過中,只有三位數字被接收。

message是一個調試信息,輸出到屏幕進行調試。

deviceonOff填充正確。

功能造成的問題:

byte device = btag-X10_TAG_OFFSET; 
byte onOff; 
char serialOut[8]; 
memset (serialOut, 0, 8); 
if(x10[device - 1]==1){ 
    onOff = 0; 
} else { 
    onOff = 1; 
} 
x10[device-1]=-2; 
sprintf(serialOut, "X10D%02i%i", device, onOff); 
Serial.println(serialOut); 
strcpy(message, serialOut); // this line appears to 'modify' the previous line 

如果我刪除最後一行,並用其交換:

message = serialOut; 

的前述串行通信就完成了!

如果我沒有,那麼在另一端的數據是垃圾(尚未解密,但顯示爲不可打印字符 - 這就是爲什麼我設置調試)。

我在想這不能相關,但平等似乎'解決'了問題。

+0

現在,刪除最後一行一切似乎都恢復正常。無線電干擾的串口線?我不確定。很明顯'message = serialOut'也會導致垃圾泄露。那麼,如何在代碼中稍後使用'serialOut'到'message'來複制? – Madivad

+0

我真的沒有得到的是爲什麼使用memcpy,memmove,strcpy後'serial.println'導致錯誤數據被髮送出去,這是我的問題的癥結 – Madivad

回答

1

因爲message是一個指針,你必須使它指向一些有效的內存。如果不這樣做,並且它是全局變量,則它將爲零(即指向NULL)並複製到它將導致未定義的行爲

如果分配它指向serialOut你,而不是有不確定的行爲另一種情況,因爲它似乎serialOut是一個局部變量,這將是超出範圍時,該功能是在返回的已定義,使得message點到未使用的內存。

兩個明顯的解決方案是讓message的數組大小足夠大以容納任何想要複製到其中的內容,或者在每次複製之前動態分配/重新分配足夠的空間。

+1

不,不要動態分配任何東西在Arduino上。它有一個非常有限的8位MCU。 – Lundin

+0

那麼定義'message'的最好方法是什麼?例如:'char message [10]; memset(message,0,10);' – Madivad

+0

@Madivad如果沒有消息將超過十個字符(包括字符串終止符),那麼這可能是最好的解決方案。哦,你不必使用'memset','sprintf' /'strcpy',所有其他字符串函數都會自動添加字符串終止符。 –

0

動態分配對於AVR來說並不是一個好主意,不是因爲它是一個8位MCU,而是因爲潛在的棧堆衝突。上面的問題是你有一個全局的char *,並且是全局的,它被初始化爲NULL [這是C標準的一部分,你無法控制這個,它由最終可執行文件的__do_clear_bss部分完成]。當你在這個NULL指針上做strcpy()這一刻,你就開始寫地址0了。現在,如果這是一臺x86機器,並且你正在應用程序級別工作,那麼軟件會因爲分段錯誤而崩潰。 AVR中沒有存儲器保護,因此strcpy()將開始將字符串複製到RAM中的地址0。如果使用絕對地址,這就直接進入寄存器文件,這意味着任何在這些寄存器上被緩存的變量現在都被丟棄了。考慮一下這個測試案例:

#include <string.h> 
int main(void) { 
    char p[] = "hello"; 
    strcpy(0, p); 
    while(1); 
} 

其編譯成:如果與AVR-gcc版本4.8.2,並調用與avr-gcc -O3 -mmcu=atmega168 -o avr.elf avr.c

從手動設置的AVR指令編譯器完成

-- snip -- 
00000096 <main>: 
-- snip -- 
    a0: cd b7   in r28, 0x3d ; 61 
    a2: de b7   in r29, 0x3e ; 62 
    a4: 86 e0   ldi r24, 0x06 ; 6 
    a6: e0 e0   ldi r30, 0x00 ; 0 
    a8: f1 e0   ldi r31, 0x01 ; 1 
    aa: de 01   movw r26, r28 
    ac: 11 96   adiw r26, 0x01 ; 1 
    ae: 01 90   ld r0, Z+ 
    b0: 0d 92   st X+, r0 
    b2: 8a 95   dec r24 
    b4: e1 f7   brne .-8   ; 0xae <main+0x18> 
    b6: be 01   movw r22, r28 
    b8: 6f 5f   subi r22, 0xFF ; 255 
    ba: 7f 4f   sbci r23, 0xFF ; 255 
    bc: 80 e0   ldi r24, 0x00 ; 0 
    be: 90 e0   ldi r25, 0x00 ; 0 
    c0: 0e 94 63 00  call 0xc6 ; 0xc6 <strcpy> 
-- snip -- 
000000c6 <strcpy>: 
    c6: fb 01   movw r30, r22 
    c8: dc 01   movw r26, r24 
    ca: 01 90   ld r0, Z+ 
    cc: 0d 92   st X+, r0 
    ce: 00 20   and r0, r0 
    d0: e1 f7   brne .-8   ; 0xca <strcpy+0x4> 
    d2: 08 95   ret 

, st指令說: 「存儲一個字節間接有或沒有從寄存器到數據空間的位移對於具有SRAM的部分,數據空間由寄存器文件,I/O存儲器和內部SRAM(以及外部SRAM,如果適用)組成, 。對於沒有SRAM的部件,數據空間僅由註冊文件組成。「

其證實了上述情況。很明顯,編譯器並不知道你拋棄了這些寄存器,並且很樂意將它們用於其他事情,甚至進一步摧毀它們。在strcpy()之後從它們中讀取也會產生問題。您可以通過創建一個緩衝區來解決問題,如果您希望它是全局的,則可以使用char message[8];。由於它是一個全局變量,因此所有元素都會自動初始化爲0.至於您的數據爲什麼會被刪除,我將不得不查看更多的代碼來確定原因。儘管如此,很可能是由於內存損壞。

乾杯。