Нещодавно я написав макрос, щоб зробити це в 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 (тому тимчасова назва змінної не потрібна):
((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES]
((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES]
__VA_ARGS__
являє все, що було надано макросу, і використовується для підвищення гнучкості того, що може бути передано (хоча і не набагато). Потім адреса цього аргументу береться та передається unsigned char
вказівнику, щоб дозволити заміну його байтів через масив[]
підписку .
Останнім своєрідним моментом є відсутність {}
брекетів. Вони не потрібні, тому що всі кроки кожного swap з'єднуються з оператором коми , що робить їх одним твердженням.
Нарешті, варто зазначити, що це не ідеальний підхід, якщо швидкість є головним пріоритетом. Якщо це важливий фактор, деякі з макросів, характерних для типу, або директив, орієнтованих на платформу, на які посилаються в інших відповідях, ймовірно, є кращим варіантом. Цей підхід, однак, є портативним для всіх типів, усіх основних платформ та обох мов C та C ++.