我总是遇到char指针,字符串,字符串和大多数指针相关的概念问题。也许我太老了 - )
全球宣布:
char * message;
serialOut
是一个非常短的8个字符的字符串,一个标识符(X10D
),然后是数据(nnn
)和null终止符。我发现通过串口发送的数据在前面被修剪,缺少了idetifier。在第一次通过时,它将是完整且正确的,但在随后的传递中,仅接收到三位数。
message
是一个调试消息,输出到屏幕进行调试。
device
和onOff
填充正确。
导致问题的功能:
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 :(得分: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.至于为什么数据会被破坏,我必须看到更多代码来确定原因。但很可能是由于内存损坏。
干杯。