Rzutowanie float na 4chary i vice versa - inne sposoby?

0

Witam.
Napisałem 2 funkcję do wysyłania i odbierania zmiennych typu float poprzez Serial port (arduino) do komunikacji z moim skryptem napisanym w języku autohotkey. Wszystko działa znakomicie tylko chciałem sobie poćwiczyć rzutowania oraz wskaźniki i jestem ciekaw czy można te 2 funkcję zapisać jeszcze w jakiś inny sposób.

void SerialWriteFloat(float val) {
  uint8_t *wsk = (uint8_t*)&val;
  Serial.write(*wsk++);
  Serial.write(*wsk++);
  Serial.write(*wsk++);
  Serial.write(*wsk++);
}

float SerialReadFloat(){
//v1
//  uint8_t ff[4];
//  ff[0] = (uint8_t)Serial.read();
//  ff[1] = (uint8_t)Serial.read();
//  ff[2] = (uint8_t)Serial.read();
//  ff[3] = (uint8_t)Serial.read();
//  return *(float*)(&ff);

//v2
  float ff;
  uint8_t *wsk=(uint8_t*)&ff;
  *wsk = (uint8_t)Serial.read(); wsk++;
  *wsk = (uint8_t)Serial.read(); wsk++;
  *wsk = (uint8_t)Serial.read(); wsk++;
  *wsk = (uint8_t)Serial.read();
  return ff;

//v3
//  float ff;
//  uint8_t *wsk=(uint8_t*)&ff;
//  *wsk++ = (uint8_t)Serial.read();
//  *wsk++ = (uint8_t)Serial.read();
//  *wsk++ = (uint8_t)Serial.read();
//  *wsk   = (uint8_t)Serial.read();
//  return ff;

//v4
//  float ff;
//  uint8_t *wsk=(uint8_t*)&ff;
//  wsk--;
//  *++wsk = (uint8_t)Serial.read();
//  *++wsk = (uint8_t)Serial.read();
//  *++wsk = (uint8_t)Serial.read();
//  *++wsk = (uint8_t)Serial.read();
//  return ff;
}
2
float x = 42;
uint8_t ff[4];
static_assert(sizeof(x) == sizeof(ff), "");
std::memcpy(ff, &x, sizeof(x));

Co do Twoich rozwiązań: v1 to UB, v2 to UB, v3 to UB, v4 to UB. Poczytaj o strict aliasing.

1

v1 to nie UB.
v2 to nie UB.
v3 to nie UB.
v4 to UB bo wsk-- jest nielegalne.

Przy czym rozwiązanie @pingwindyktator jest ładniejsze, ale prawdopodobnie masz funkcje do obsługi serial portu przyjmujące wskaźnik + długość, tak by było najłatwiej i najczytelniej.

0

o ile rozumiem to strict aliasing oznacza, że powinno się unikać aby więcej niż jedna zmienna odnosiła się do tego samego adresu w pamięci gdyż kompilator może nie rozpoznać ale nie koniecznie, że zmienna A została zmodyfikowana zmienną B przez co może to zostać rozpoznane jako zupełnie 2 rożne zmienne. I jeśli dobrze wnioskuję to można tego teoretycznie uniknąć stosując zmienne typu volatile (pomijam tu kwestię wydajności)?
Tu nasuwa mi się także pytanie czy jeśli 2 zmienne mają ten sam adres lecz nie wykonujemy żadnego zapisu pod wskazany adres, tylko odczyt to można się tym nie przejmować?
Odnosząc się do moich przykładów to owe zjawisko może zajść w v2,v3,v4 natomiast v1 powinno być OK jeśli nic nie przeinaczyłem.

Przykłady które napisałem to było raczej moje wyobrażenie jak mniej więcej bym się do tego zabrał pisząc kod w asm ale teraz widzę, że czasami kompilator może nie zrobić tego co oczekuję.

co do wsk-- to nie rozumiem czemu jest to nielegalne. Teoretycznie dopóki mam pewność, że nie znajdę się tam gdzie nie trzeba to powinno być OK (tutaj pomijam sensowność tego w moim przykładzie, po prostu testowałem "before/post" inkrementację)

2

Niestety, nie wszystkie zasady w C++ są sensowne - a już na pewno nie na pierwszy rzut oka.

Strict aliasing polega na tym, że zmienne różnych typów nie okupują tego samego miejsca w pamięci (z wyjątkami), wobec czego kompilator może założyć, że zmiana np. zmiennej typu int nigdy nie wpłynie na zmianę wartości float, co zezwala na bardzo konkretne optymalizacje.

Jednym z wyjątków jest castowanie na char (i std::byte), we wszystkich jego odmianach, w tym uint8_t. Jeśli taki typ istnieje, to jest on aliasem na jeden z charów.

czy jeśli 2 zmienne mają ten sam adres lecz nie wykonujemy żadnego zapisu pod wskazany adres, tylko odczyt to można się tym nie przejmować?

I tak i nie. Język wyraźnie zabrania na odnoszenie się do danych za pomocą niekompatybilnych typów, z wyjątkami. Jeśli jesteś w takim wyjątku to Twoje rozumowanie jest sensowne. W przeciwnym wypadku raczej też, ale formalnie to UB, więc nie masz gwarancji, ze zadziała tak jak myślisz.

co do wsk-- to nie rozumiem czemu jest to nielegalne.

Bo standard tego zakazuje, tyle. Konkretnie: tutaj

0

co do wsk-- to nie rozumiem czemu jest to nielegalne.

kq napisał(a):

Bo standard tego zakazuje, tyle. Konkretnie: tutaj

@smarq: A pomyśl sam, dlaczego to nielegalne -- bo (akurat ten) zakaz standardu ma sens. :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1