Niedawno napisałem makro, aby to zrobić w C, ale jest równie poprawne w C ++:
#define REVERSE_BYTES(...) do for(size_t REVERSE_BYTES=0; REVERSE_BYTES<sizeof(__VA_ARGS__)>>1; ++REVERSE_BYTES)\
((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES],\
((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES],\
((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES];\
while(0)
Akceptuje dowolny typ i odwraca bajty w przekazanym argumencie. Przykładowe zastosowania:
int main(){
unsigned long long x = 0xABCDEF0123456789;
printf("Before: %llX\n",x);
REVERSE_BYTES(x);
printf("After : %llX\n",x);
char c[7]="nametag";
printf("Before: %c%c%c%c%c%c%c\n",c[0],c[1],c[2],c[3],c[4],c[5],c[6]);
REVERSE_BYTES(c);
printf("After : %c%c%c%c%c%c%c\n",c[0],c[1],c[2],c[3],c[4],c[5],c[6]);
}
Które wydruki:
Before: ABCDEF0123456789
After : 8967452301EFCDAB
Before: nametag
After : gateman
Powyższe jest doskonale możliwe do kopiowania / wklejania, ale tutaj dużo się dzieje, więc podzielę się tym, jak to działa kawałek po kawałku:
Pierwszą godną uwagi rzeczą jest to, że całe makro jest zamknięte w do while(0)
bloku. To jest powszechny idiom umożliwiający normalne użycie średnika po makrze.
Następna w kolejce jest użycie zmiennej o nazwie REVERSE_BYTES
jakfor
licznik pętli jest. Nazwa samego makra jest używana jako nazwa zmiennej, aby upewnić się, że nie koliduje on z innymi symbolami, które mogą być w zasięgu wszędzie tam, gdzie makro jest używane. Ponieważ nazwa jest używana w ramach rozwinięcia makra, nie zostanie ponownie rozwinięta, gdy zostanie użyta tutaj jako nazwa zmiennej.
W for
pętli są dwa bajty, do których się odwołujemy, i zamiana XOR (więc nazwa zmiennej tymczasowej nie jest wymagana):
((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES]
((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES]
__VA_ARGS__
reprezentuje to, co zostało dane makrze, i służy do zwiększenia elastyczności tego, co może zostać przekazane (choć niewiele). Adres tego argumentu jest następnie pobierany i przesyłany do unsigned char
wskaźnika, aby umożliwić zamianę jego bajtów poprzez []
indeksowanie tablicy .
Ostatnim szczególnym punktem jest brak {}
nawiasów klamrowych. Nie są one konieczne, ponieważ wszystkie kroki w każdej zamianie są połączone z operatorem przecinkowym , co czyni je jedną instrukcją.
Na koniec warto zauważyć, że nie jest to idealne podejście, jeśli priorytetem jest prędkość. Jeśli jest to ważny czynnik, niektóre makra specyficzne dla typu lub dyrektywy specyficzne dla platformy, o których mowa w innych odpowiedziach, są prawdopodobnie lepszą opcją. Takie podejście jest jednak przenośne dla wszystkich typów, wszystkich głównych platform oraz języków C i C ++.