Czy istnieje programowy sposób na wykrycie, czy korzystasz z architektury big-endian czy little-endian? Muszę być w stanie napisać kod, który będzie wykonywany w systemie Intel lub PPC i używać dokładnie tego samego kodu (tj. Bez kompilacji warunkowej).
c++
algorithm
endianness
Jay T.
źródło
źródło
Odpowiedzi:
Nie podoba mi się metoda oparta na pisaniu na czcionkach - często będzie ostrzegana przez kompilator. Właśnie po to są związki!
Zasada jest równoważna przypadkowi typu sugerowanemu przez innych, ale jest to jaśniejsze - i zgodnie z C99, gwarantuje się, że jest poprawna. gcc woli to w porównaniu do bezpośredniego rzutowania wskaźnikiem.
Jest to również znacznie lepsze niż naprawianie endianizmu w czasie kompilacji - w przypadku systemu operacyjnego obsługującego wiele architektur (na przykład gruby plik binarny w systemie Mac OS X), będzie to działać zarówno w przypadku ppc / i386, jak i bardzo łatwo zepsuć wszystko inaczej .
źródło
CHAR_BIT != 8
?Możesz to zrobić, ustawiając int i maskując bity, ale prawdopodobnie najłatwiejszym sposobem jest po prostu użycie wbudowanych operacji konwersji bajtów sieciowych (ponieważ kolejność bajtów w sieci jest zawsze duża).
Nieznaczne skrzypki mogą być szybsze, ale ten sposób jest prosty, prosty i całkiem niemożliwy do zepsucia.
źródło
BSWAP
operacji.Zobacz ten artykuł :
źródło
Możesz użyć,
std::endian
jeśli masz dostęp do kompilatora C ++ 20, takiego jak GCC 8+ lub Clang 7+.Uwaga:
std::endian
rozpoczęła się<type_traits>
, ale została przeniesiona do<bit>
w 2019 roku w Kolonii spotkania. GCC 8, Clang 7, 8 i 9 mają go,<type_traits>
podczas gdy GCC 9+ i Clang 10+ mają go<bit>
.źródło
Zwykle odbywa się to w czasie kompilacji (szczególnie ze względu na wydajność) przy użyciu plików nagłówkowych dostępnych z kompilatora lub utwórz własny. W systemie Linux masz plik nagłówkowy „/usr/include/endian.h”
źródło
Zaskoczyłem, że nikt nie wspomniał o makrach, które domyślnie definiuje procesor wstępny. Chociaż będą się różnić w zależności od platformy; są o wiele czystsze niż pisanie własnych testów endian.
Na przykład; jeśli spojrzymy na wbudowane makra zdefiniowane przez GCC (na maszynie X86-64):
Na maszynie PPC dostaję:
(
:| gcc -dM -E -x c -
Magia drukuje wszystkie wbudowane makra).źródło
echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'
nic nie zwraca, podczas gdy gcc 3.4.3 (w/usr/sfw/bin
każdym razie) w Solarisie ma taką definicję. Widziałem podobne problemy na VxWorks Tornado (gcc 2.95) -vs- VxWorks Workbench (gcc 3.4.4).Ehm ... Zaskakuje mnie, że nikt nie zdawał sobie sprawy, że kompilator po prostu zoptymalizuje test i poda ustalony wynik jako wartość zwracaną. To sprawia, że wszystkie powyższe przykłady kodu są praktycznie bezużyteczne. Jedyne, co zostanie zwrócone, to endianness w czasie kompilacji! I tak, przetestowałem wszystkie powyższe przykłady. Oto przykład z MSVC 9.0 (Visual Studio 2008).
Czysty kod C.
Demontaż
Być może możliwe jest wyłączenie DOWOLNEJ optymalizacji czasu kompilacji tylko dla tej funkcji, ale nie wiem. W przeciwnym razie może być możliwe zakodowanie go w zestawie, chociaż nie jest to przenośne. I nawet wtedy można to zoptymalizować. To sprawia, że myślę, że potrzebuję naprawdę kiepskiego asemblera, zaimplementuj ten sam kod dla wszystkich istniejących procesorów / zestawów instrukcji i cóż ... nieważne.
Również ktoś tutaj powiedział, że endianizm nie zmienia się w czasie wykonywania. ŹLE. Istnieją maszyny typu bi-endian. Ich endianizm może się różnić podczas wykonywania. RÓWNIEŻ istnieje nie tylko Little Endian i Big Endian, ale także inne endianizmy (co za słowo).
Nienawidzę i uwielbiam jednocześnie kodować ...
źródło
Zadeklaruj zmienną int:
Teraz użyj wskaźników char * do różnych jego części i sprawdź, co jest w tych częściach.
W zależności od tego, który punkt wskazuje na bajt 0xFF, możesz teraz wykryć endianizm. Wymaga to sizeof (int)> sizeof (char), ale jest to z pewnością prawda dla omawianych platform.
źródło
Aby uzyskać więcej informacji, możesz przeczytać ten artykuł dotyczący projektu kodowego Podstawowe pojęcia na temat Endianness :
źródło
Sposobem C ++ było użycie boosta , w którym kontrole i rzuty preprocesora są dzielone w bardzo dokładnie przetestowanych bibliotekach.
Biblioteka Predef (boost / predef.h) rozpoznaje cztery różne rodzaje endianizmu .
Endian Biblioteka miała zostać złożone w standardzie C ++ i obsługuje szeroki zakres operacji na danych endian wrażliwej.
Jak stwierdzono w odpowiedziach powyżej, Endianness będzie częścią c ++ 20.
źródło
O ile nie używasz frameworka, który został przeniesiony do procesorów PPC i Intel, będziesz musiał wykonać kompilacje warunkowe, ponieważ platformy PPC i Intel mają zupełnie inną architekturę sprzętową, potoki, magistrale itp. To sprawia, że kod asemblera jest zupełnie inny dwójka.
Jeśli chodzi o znalezienie endianizmu, wykonaj następujące czynności:
Otrzymasz albo tempChar na 0x12, albo 0x34, z którego poznasz endianness.
źródło
stdint.h
i wykorzystajint16_t
w przyszłości dowód, że short jest inny na innej platformie.Zrobiłbym coś takiego:
Wzdłuż tych linii otrzymasz efektywną czasowo funkcję, która wykonuje obliczenia tylko raz.
źródło
Jak wspomniano powyżej, używaj sztuczek związkowych.
Jest jednak kilka problemów z tymi zalecanymi powyżej, w szczególności, że niewyrównany dostęp do pamięci jest notorycznie powolny w przypadku większości architektur, a niektóre kompilatory nawet nie rozpoznają takich stałych predykatów, chyba że dopasują słowa.
Ponieważ zwykły test endian jest nudny, oto funkcja (szablon), która zmienia wejście / wyjście dowolnej liczby całkowitej zgodnie z twoją specyfikacją, niezależnie od architektury hosta.
Stosowanie:
Aby przekonwertować dane dane z Endian na Host, użyj:
host = endian(source, endian_of_source)
Aby przekonwertować z hosta endian na dany endian, użyj:
output = endian(hostsource, endian_you_want_to_output)
Wynikowy kod jest tak szybki, jak pisanie zestawu ręcznego na clang, na gcc jest nieco wolniejszy (rozwinięty i, <<, >>, | dla każdego bajtu), ale nadal przyzwoity.
źródło
źródło
#define IS_BIGENDIAN() (*((char*) &((int){ 0x00ff })) == (0x00))
Nie używaj
union
!C ++ nie zezwala na pisanie typu za pomocą
union
s!Czytanie z pola unii, które nie było ostatnim polem, do którego napisano, jest niezdefiniowanym zachowaniem !
Wiele kompilatorów obsługuje to jako rozszerzenia, ale język nie daje żadnej gwarancji.
Zobacz tę odpowiedź, aby uzyskać więcej informacji:
https://stackoverflow.com/a/11996970
Istnieją tylko dwie poprawne odpowiedzi, które z pewnością są przenośne.
Pierwszą odpowiedzią, jeśli masz dostęp do systemu obsługującego C ++ 20,
jest użycie
std::endian
z<type_traits>
nagłówka.(W momencie pisania C ++ 20 nie został jeszcze wydany, ale chyba że coś wpłynie na
std::endian
włączenie, będzie to preferowany sposób testowania endianizmu w czasie kompilacji od C ++ 20 wzwyż.)C ++ 20 i więcej
Przed wersją C ++ 20 jedyną prawidłową odpowiedzią jest zapisanie liczby całkowitej, a następnie sprawdzenie pierwszego bajtu za pomocą znakowania punktowego.
W przeciwieństwie do użycia
union
s, jest to wyraźnie dozwolone przez system typów C ++.Ważne jest również, aby pamiętać, że dla optymalnej przenośności
static_cast
należy użyć,ponieważ
reinterpret_cast
jest zdefiniowana implementacja.C ++ 11 i dalsze
C ++ 11 i więcej (bez wyliczenia)
C ++ 98 / C ++ 03
źródło
To jest inne rozwiązanie. Podobne do rozwiązania Andrew Hare.
źródło
niesprawdzone, ale moim zdaniem powinno to działać? bo to będzie 0x01 na małym endianie i 0x00 na dużym endianie?
źródło
Ogłosić:
Mój początkowy post został niepoprawnie zadeklarowany jako „czas kompilacji”. Nie jest, w obecnym standardzie C ++ jest to wręcz niemożliwe. Constexpr NIE oznacza, że funkcja zawsze wykonuje obliczenia w czasie kompilacji. Dzięki Richard Hodges za korektę.
czas kompilacji, bez makr, rozwiązanie C ++ 11 constexpr:
źródło
Możesz to również zrobić za pomocą preprocesora, używając czegoś takiego jak plik nagłówkowy boost, który można znaleźć w boost endian
źródło
O ile nagłówek endian nie jest tylko GCC, dostarcza makr, których możesz użyć.
źródło
__BYTE_ORDER__
,__ORDER_LITTLE_ENDIAN__
i__ORDER_BIG_ENDIAN__
?Jeśli nie chcesz kompilacji warunkowej, możesz po prostu napisać niezależny kod endian. Oto przykład (wzięty z Roba Pike'a ):
Odczytywanie liczby całkowitej przechowywanej w little-endian na dysku w sposób niezależny od endian:
Ten sam kod, starając się uwzględnić endianizm maszyny:
źródło
źródło
Co powiesz na to?
źródło
Oto kolejna wersja C. Definiuje makro wywoływane
wicked_cast()
do wstawiania tekstu za pomocą literałów C99 i niestandardowego__typeof__
operatora.Jeśli liczby całkowite są wartościami jednobajtowymi, endianness nie ma sensu i zostanie wygenerowany błąd czasu kompilacji.
źródło
Sposób, w jaki kompilatory C (przynajmniej wszyscy, których znam) działa na endianizm, musi być ustalony w czasie kompilacji. Nawet w przypadku procesorów biendian (takich jak ARM i MIPS) musisz wybrać endianness w czasie kompilacji. Co więcej, endianność jest zdefiniowana we wszystkich popularnych formatach plików dla plików wykonywalnych (takich jak ELF). Chociaż możliwe jest stworzenie binarnego obiektu blob z kodu biandian (może dla niektórych exploitów serwera ARM?), Prawdopodobnie należy to zrobić podczas montażu.
źródło
Jak zauważył Coriiander, większość (jeśli nie wszystkie) tych kodów zostanie zoptymalizowana w czasie kompilacji, więc wygenerowane pliki binarne nie sprawdzą „endianizmu” w czasie wykonywania.
Zaobserwowano, że dany plik wykonywalny nie powinien działać z dwoma różnymi kolejnymi bajtami, ale nie mam pojęcia, czy tak jest zawsze, i wydaje mi się, że to sprawdzanie w czasie kompilacji. Więc zakodowałem tę funkcję:
MinGW nie był w stanie zoptymalizować tego kodu, mimo że optymalizuje inne kody tutaj. Wydaje mi się, że dzieje się tak, ponieważ zostawiam „losową” wartość, która została przydzielona do mniejszej pamięci bajtowej (co najmniej 7 jej bitów), więc kompilator nie może wiedzieć, co to jest ta losowa wartość i nie optymalizuje funkcja jest wyłączona.
Zakodowałem również tę funkcję, aby kontrola była wykonywana tylko raz, a zwracana wartość jest przechowywana do następnych testów.
źródło
0x7FE
? Pomalloc()
co w ogóle korzystać? to marnotrawstwo. I_BE
jest (choć niewielki) wyciek pamięci i warunki wyścigu, które czekają na nadejście, korzyści dynamicznego buforowania wyniku nie są warte kłopotu. Zamiast tego zrobiłbym coś takiego:static const uint16_t teste = 1; int is_little_endian() { return (0x01 == ((uint8_t*)&teste)[0]); } int is_big_endian() { return (0x01 == ((uint8_t*)&teste)[1]); }
prosty i skuteczny, a znacznie mniej pracy do wykonania w czasie wykonywania.volatile
lub#pragma
itp.chociaż nie ma szybkiego i standardowego sposobu na określenie tego, wygeneruje to:
źródło
Zobacz Endianness - ilustracja kodu poziomu C.
źródło
Przeglądałem podręcznik: System komputerowy: perspektywa programisty i istnieje problem z określeniem, który endian jest to program C.
Użyłem funkcji wskaźnika, aby to zrobić w następujący sposób:
Ponieważ int zajmuje 4 bajty, a char zajmuje tylko 1 bajt. Możemy użyć wskaźnika char, aby wskazać int z wartością 1. Zatem jeśli komputer jest małym endianem, char, na który wskazuje wskaźnik char, ma wartość 1, w przeciwnym razie jego wartość powinna wynosić 0.
źródło