Допустим у нас есть функция, которая принимает в себя указатель. Мы знаем, что в указателе лежит нуль-терминальная строка, а за ней 4-байтное целое. Задача — вывести в консоль строку и целое. Решить можно вот так:
void foo(void* data_ptr)
{
//Ставим указатель на строку на начало данных
char* str = (char*)data_ptr;
//А указатель на целое смещаем на длину строки и еще один байт
int* value = (int*)(str+strlen(str)+1);
//и выводим содержимое указателей
printf("%s %d", str, *value);
}
Довольно тривиальная задача, не так ли? Проверяем на компе (x86), все ОК. Загружаем на борду с ARM. И, не успев выстрелить себе в ногу, наступаем на грабли. В зависимости от содержания строки, целое значение выводится то нормальным, то кривым. Поверяем указатели, проверяем память, на которые они указывают. Все в норме.
Подмечаем, что целое выводится ровно, когда длина строки равна 3, 7, 11, ..., 4*n-1. Ага. По внимательней смотрим на память и на вывод в «кривых» случаях. Например, если память выглядит так:
Адрес:
|0x00|0x01|0x02|0x03|0x04|0x05|0x06|0x07|0x08|
Данные:
|0x31|0x31|0x31|0x31|0x00|0x01|0x00|0x00|0x00|
На выходе мы получаем строку «1111» и целое 0x00000100 вместо 0x00000001.
Вывод: Несмотря на то, что выраджением *value мы обращаемся по указателю 0x05, данные нам возвращаются как-будто обращение происходит по указателю 0x04 (или другому кратному 4).
Так как правильно решить такую задачу? А вот так:
void foo(void* data_ptr)
{
int value; //Выделяем переменную на стеке
char* str = (char*)data_ptr;
memcpy(&value, str, str+strlen(str)+1)); //копируем в нее данные
printf("%s %d", str, value); //выводим данные
}
В таком случае все всегда на своих местах.
Спасибо за внимание!
Автор: CasualLinux