strcpy似乎搞乱了以前的Arduino序列

时间:2014-01-16 07:00:33

标签: c++ arduino avr strcpy

我总是遇到char指针,字符串,字符串和大多数指针相关的概念问题。也许我太老了 - )

全球宣布:

char * message;

serialOut是一个非常短的8个字符的字符串,一个标识符(X10D),然后是数据(nnn)和null终止符。我发现通过串口发送的数据在前面被修剪,缺少了idetifier。在第一次通过时,它将是完整且正确的,但在随后的传递中,仅接收到三位数。

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;

之前的串行通信已完成!

如果我没有,那么另一端的数据是垃圾(尚未解密,但显示为不可打印的字符 - 这就是我设置调试的原因)。

我认为这无关紧要,但平等似乎“解决”了问题。

2 个答案:

答案 0 :(得分:1)

因为message是一个指针,你必须指向一些有效的内存。如果你不这样做,并且它是一个全局变量,那么它将为零(即指向NULL)并且复制到它将导致未定义的行为

如果指定指向serialOut,则会出现另一种未定义行为的情况,因为serialOut似乎是一个局部变量,它将不在范围当它定义的函数返回时,使message指向未使用的内存。

两个明显的解决方案是使message数组足够大,以容纳您想要复制到其中的任何内容,或者在每次复制之前动态分配/重新分配足够的空间。

答案 1 :(得分:0)

动态分配对AVR来说不是一个坏主意,不是因为它是8位MCU,而是因为潜在的堆栈堆冲突。上面的问题是你有一个全局char *,并且是全局的,它被初始化为NULL [这是C标准的一部分,你无法控制它,它由{{1}完成最终可执行文件的部分]。在__do_clear_bss指针上执行strcpy()的那一刻,您开始在地址0处写入。现在,如果这是一台x86计算机并且您正在应用程序级别工作,那么软件会因为分段错误而崩溃。 AVR中没有内存保护,因此NULL将很乐意开始将字符串复制到RAM中的地址0。如果使用绝对地址,这将直接进入寄存器文件,这意味着现在已经删除了缓存在这些寄存器上的任何变量。考虑这个测试用例:

strcpy()

编译为:

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

如果使用avr-gcc版本4.8.2并使用-- 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

调用编译器

从AVR指令集手册中,st指令说: “存储一个字节,间接或不存在从寄存器到数据空间的位移。对于带SRAM的部分,数据空间由寄存器文件,I / O存储器和内部SRAM(以及外部SRAM,如果适用)组成。对于没有SRAM的部件,数据空间仅由注册文件组成。“

证实了上述情况。显然,编译器并不真正知道你破坏了寄存器,并且很乐意将它们用于其他事情,甚至进一步摧毁它们。在strcpy()之后从它们中读取也会产生问题。您可以通过将缓冲区设置为avr-gcc -O3 -mmcu=atmega168 -o avr.elf avr.c来解决问题,如果您希望将其设置为全局缓冲区。由于它是一个全局变量,所有元素都会自动初始化为0.至于为什么数据会被破坏,我必须看到更多代码来确定原因。但很可能是由于内存损坏。

干杯。