Я недавно написал макрос для этого в C, но он одинаково действителен в 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)
Он принимает любой тип и переворачивает байты в переданном аргументе.
Пример использования:
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]);
}
Какие отпечатки:
Before: ABCDEF0123456789
After : 8967452301EFCDAB
Before: nametag
After : gateman
Вышесказанное прекрасно подходит для копирования / вставки, но здесь многое происходит, поэтому я расскажу, как это работает по частям:
Первое, что следует отметить, это то, что весь макрос заключен в блок do while(0)
. Это общая идиома , разрешающая нормальное использование точки с запятой после макроса.
Далее следует использовать переменную с именем REVERSE_BYTES
в качестве счетчика цикла for
. Имя самого макроса используется в качестве имени переменной, чтобы гарантировать, что он не конфликтует с любыми другими символами, которые могут находиться в области видимости везде, где используется макрос. Поскольку имя используется в расширении макроса, оно не будет расширено при использовании в качестве имени переменной здесь.
В цикле for
есть два байта, на которые ссылаются, и XOR swapped (поэтому временное имя переменной не требуется):
((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES]
((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES]
__VA_ARGS__
представляет все, что было дано макросу, и используется для увеличения гибкости того, что может быть передано (хотя и ненамного). Адрес этого аргумента затем берется и приводится к указателю unsigned char
, чтобы разрешить обмен его байтов с помощью массива []
с подпиской.
Последняя особенность - отсутствие {}
скобок. В них нет необходимости, поскольку все шаги в каждом свопе объединяются с помощью оператора запятой , что делает их одним оператором.
Наконец, стоит отметить, что это не идеальный подход, если скорость является главным приоритетом. Если это важный фактор, некоторые из макросов, специфичных для типа, или директив, специфичных для платформы, на которые есть ссылки в других ответах, вероятно, являются лучшим вариантом. Однако этот подход переносим на все типы, на все основные платформы и на языки C и C ++.