У меня всегда были проблемы с концепцией указателей на символы, строк, строк и большинства связанных с указателем вещей. Может быть, я слишком стар для этого 😉
объявлено глобально:
char * message;
serialOut
очень короткая 8-символьная строка, идентификатор (X10D
), а затем данные (nnn
) и нулевой терминатор. Я нахожу данные, отправленные через серийный номер, для обрезки спереди, пропуская идентификатор. При первом прохождении оно будет полным и правильным, но при последующем прохождении были получены только три цифры.
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;
предыдущая последовательная связь завершена!
Если у меня нет ни того, ни другого, то данные на другом конце являются мусором (еще не расшифрованы, но отображаются как непечатные символы — вот почему я настраиваю отладку).
Я думаю, что это не может быть связано, но равенство, похоже, решает проблему.
Так как message
это указатель, который вы должны указать на некоторую действительную память. Если вы этого не сделаете, и это глобальная переменная, то она будет равна нулю (т. Е. Указывает на NULL
) и копирование на него приведет к неопределенное поведение.
если ты назначать это указывать на serialOut
вместо этого у вас есть еще один случай неопределенного поведения, так как кажется, что serialOut
является локальной переменной, и она выйдет из области видимости, когда функция, определенная в возврате, message
указать на неиспользованную память.
Два очевидных решения должны сделать message
массив достаточно большого размера, чтобы вместить в него все, что вы захотите скопировать, или динамически выделять / перераспределять достаточно места каждый раз перед копированием в него.
Динамическое распределение является плохой идеей для AVR не потому, что это 8-битный MCU, а из-за потенциальных коллизий стека и кучи. Проблема выше в том, что у вас есть глобальный символ *, и, будучи глобальным, он инициализируется NULL
[это часть стандарта C, вы не можете это контролировать, это делается __do_clear_bss
раздел финального исполняемого файла. В тот момент, когда вы делаете strcpy()
на этом NULL
Указатель, вы начинаете писать по адресу 0. Теперь, если это был компьютер с архитектурой x86, и вы работали на уровне приложения, программное обеспечение зависало бы из-за ошибки сегментации. В AVR нет защиты памяти, поэтому strcpy()
с радостью начнет копировать строку по адресу 0 в ОЗУ. Это попадает прямо в файл регистров, если используются абсолютные адреса, что означает, что любые переменные, которые были кэшированы в этих регистрах, теперь удаляются. Рассмотрим этот тестовый пример:
#include <string.h>
int main(void) {
char p[] = "hello";
strcpy(0, p);
while(1);
}
который компилируется в:
-- 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-gcc 4.8.2 и вызовом компилятора с avr-gcc -O3 -mmcu=atmega168 -o avr.elf avr.c
Из руководства по набору инструкций AVR st инструкция говорит:
«Сохраняет один байт, косвенный с или без смещения из регистра в пространство данных. Для частей с SRAM пространство данных состоит из файла регистра, памяти ввода-вывода и внутренней SRAM (и внешней SRAM, если применимо). Для частей без SRAM, пространство данных состоит только из регистрационного файла. «
что подтверждает вышесказанное. Очевидно, что компилятор на самом деле не знает, что вы уничтожили регистры, и с радостью будет использовать их для других вещей, уничтожая их еще дальше. Чтение с них после strcpy () также приведет к проблемам. Вы можете решить проблему, сделав буфер, если вы хотите, чтобы он был глобальным, как char message[8];
, Поскольку это глобальная переменная, все элементы автоматически инициализируются равными 0. Что касается того, почему ваши данные попадают в мусор, я должен был бы увидеть больше вашего кода, чтобы определить, почему. Скорее всего, из-за повреждения памяти.
Приветствия.