LINUX.ORG.RU

C++, ARM, преобразование адресов


0

0

Есть такая программка:

#include <cstdio>

int main(int argc, char **argv)
{
    char s[] = { 1, 1, 1, 77, 135, 29, 67 };

    int offset = 3;

    float d_value = *(float *)(s+offset);

    printf("%f\n", d_value);

    return 0;
}

/*****************************/

Компилирую на свой машине:

# g++ -O2 -o test test.cpp
# ./test
157.528519

С компилятором что 4.1.2 что 3.4.6 работает нормально.

Теперь кросскомпилирую для ARM (atmel rm9200, little endian, кросскомпилятор gcc-3.4.3):

# arm-linux-g++ -O2 -o test test.cpp

На приборе через терминал запускаю:

# ./test 
0.000000

************************************************

Почему во втором случае получается 0.0 ? 

Спасибо.
anonymous

Ответ на: комментарий от anonymous

не вижу разницы.

Адрес вычисляется правильно, а вот во время _взятия значения_ по указателю, во втором случае происходит непонятное выравнивание и значение d_value получается неверным.

anonymous
()
Ответ на: комментарий от anonymous

вот так ты добьешься тогоже результа и на x86 попробуй на ARM

#include <cstdio>

int main(int argc, char **argv)
{
    char s[] = { 67, 29, 135, 77, 1, 1, 1 }; 

    int offset = 3;

    float d_value = *(float *)(s+offset);

    printf("%f\n", d_value);

    return 0;
}

anonymous
()
Ответ на: комментарий от anonymous

>>вот так ты добьешься тогоже результа и на x86 попробуй на ARM

это не работает ни там ни там.

anonymous
()
Ответ на: комментарий от anonymous

>%f -- это печать даблов (man printf) То что у тебя печатается --- это мусор

Бля, забыл, что для функций с переменным числом параметров float преобразуется в double. Беру свои слова назад.

anonymous
()
Ответ на: комментарий от anonymous

>>%f -- это печать даблов (man printf) То что у тебя печатается --- это мусор

вы бы хоть разобрались. В данном конретном случае это отслеживается компилятором, и является ПОЛНОСТЬЮ корректым значением. Сейчас сделал gcc -S для этого случая, и для случая с явным преобразованием (double)d_value. Листинги получились абсолютно одинаковые.

anonymous
()
Ответ на: комментарий от anonymous

Попробуй выключить оптимизацию, т.к. при оптимизации компилятор может, например, выкинуть инициализацию массива s и будет прав (с точки зрения стандарта), поскольку согласно стандарту поведение конструкции *(float *)(s+offset), где s -- массив char, не определено. А больше он нигде не используется.

anonymous
()

А очень надо именно так делать? Это ведь совершенно непереносимый код. Если (предположу) стоит задача обмена данными в виде чисел с плавающей точкой между контроллером и управляющей машиной, то надо передавать их в строковом виде, потом конвертировать.

anonymous
()
Ответ на: комментарий от anonymous

>>Попробуй выключить оптимизацию

не помогло.

Сейчас попробовал не брать значение, а записывать. Т.е.

*(float *)(s+offset) = ...;

потом побайтно вывел массив s на экран. Значение реально записалось не 3-го байта, а с нулевого. Т.е., как я уже говорил, во время взятия значения по адресу происходит выравнивание самого адреса. Вопрос почему.

P.S. также проверил бинарное представление float на x86 и на arm'e - полностью совпадают.

P.P.S. проблема также проявляется и для int/long, т.е. для 4-х байтных чисел, для short не воспроизводится.

anonymous
()
Ответ на: комментарий от anonymous

>>Это ведь совершенно непереносимый код. Если (предположу) стоит задача обмена данными в виде чисел с плавающей точкой между контроллером и управляющей машиной, то надо передавать их в строковом виде, потом конвертировать.

Arm'овский прибор считывает данные с другого прибора в режиме ModBUS RTU (можно и ASCII, но сейчас не об этом). В конечном итоге они кладуться на диск в текстовые файлы, так что проблем не будет.

anonymous
()

Судя по моему опыту работы с ARM-ами, попытка взять что-то по невыровненному адресу, в 99% случаев приводит к считыванию мусора, либо к аппаратным exception-ам (на WM девайсах).

ARM архитектура не позволяет образатся к 4х байтным данным, не выровненным по 4х байтной границе, вам, увы, прийдется извращатся, вычитывая 2 4х байтных слова, и высчитывая корректное значение, используя операции битового сдвига и and / or битовые.

quarck
()

Простите за неконструктивный комментарий, но ...

... за такой код нужно к позорному столбу ставить. (Про выравнивание уже
сказали, не буду повторяться).

Dselect ★★★
()

А если попробовать так:

char s[] = { 77, 135, 29, 67 }

float d_value;

void *ptr = &d_value;

memcpy(ptr, s, sizeof(float));

printf("%f\n", d_value);

anonymous
()
Ответ на: комментарий от anonymous

На некоторых системах даже memcpy бесится с невыровненных адресов (хотя в вашем примере они будут выровненными).

quarck
()

на арме адресс должен делиться на 4, в последних моделях послобление и адресс может делиться на 2

fghj ★★★★★
()
Ответ на: комментарий от fghj

есть опция gcc, чтобы генерировать предупреждения на невалидный код:
$ cat 1.cpp
#include <cstdio>

int main(int argc, char **argv)
{
char s[] = { 1, 1, 1, 77, 135, 29, 67 };

int offset = 3;

float d_value = *(float *)(s+offset);

printf("%f\n", d_value);

return 0;
}
$ g++ -Wall -Wcast-align 1.cpp
$
$ arm-linux-g++ -Wall -Wcast-align 1.cpp
1.cpp: In function 'int main(int, char**)':
1.cpp:9: warning: cast from 'char*' to 'float*' increases required alignment of target type

fghj ★★★★★
()
Ответ на: комментарий от anonymous

Ты не ответил на мой ответ. (C)

> как тогда правильно?

А Вы расскажите -- чего нужно сделать-то? Заново изобрести atoi?
Заново изобрести operator>>? Заново изобрести XDR? Или еще что-то?

Вы вопрос задайте. (C)

Dselect ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.