My task is to convert a data file from big endian to little endian & vice versa using C. I have been looking online for about 3 hours now for other examples and reading my text book, however I am so stuck on how to even start this function.
So far I have the order of events correct (1 through 4) but inside my convert_and_save
function do I have to create a char array using → char buffer[4];
?
Can someone please help me? even if you just give me clues on what to look up, I would greatly appreciate it.
I need to write a function called:
void convert_and_save(struct record item, FILE * output_handle, int number);
inside this function I do the following series of steps:
Convert the integer into an array of chars using:
int integer_to_characters(int number, char * buffer) { memcpy(buffer, &number, 4); }
Reverse the order of the chars in that array.
Convert the array of chars back to an integer using:
int characters_to_integer(char * buffer) { int result; memcpy(&result, buffer, 4); return result; }
write the converted record on the output file using:
void save_record(FILE * file_handle, struct record a) { char output_buffer[size_of_record]; integer_to_characters(a.age, &(output_buffer[0])); memcpy(&(output_buffer[4]), a.name, 12); integer_to_characters(a.department, &(output_buffer[16])); fwrite(output_buffer, sizeof(char), size_of_record, file_handle); }
These are not standard function (these are gcc extensions), but you may be able to use them:
— Built-in Function: uint16_t __builtin_bswap16 (uint16_t x) Returns x with the order of the bytes reversed; for example, 0xaabb becomes 0xbbaa. Byte here always means exactly 8 bits.
— Built-in Function: uint32_t __builtin_bswap32 (uint32_t x) Similar to __builtin_bswap16, except the argument and return types are 32 bit.
— Built-in Function: uint64_t __builtin_bswap64 (uint64_t x) Similar to __builtin_bswap32, except the argument and return types are 64 bit.
If you can use these, chances are it will result in more optimal code for your platform, if not, good job, submit a patch to gcc :)
Clang also has __builtin_bswap16() __builtin_bswap32() __builtin_bswap64()
Visual Studio
unsigned short _byteswap_ushort (
unsigned short val
);
unsigned long _byteswap_ulong (
unsigned long val
);
unsigned __int64 _byteswap_uint64 (
unsigned __int64 val
);
ICC has _bswap16, _bswap and _bswap64
*need further reference
This one of the functions I wrote for the Parrot VM, you can download byteorder.c from parrotcode.org. There are probably shorter ways to do it, but this works for different size integers, and we had a macro to detect the byteorder of the platform in PARROT_BIGENDIAN, you can discard all that. Also, as mentioned above, you can search out htonl() which is a nop on bigendian hardware, but converts on little endian (just grab a Linux x86 implementation)
INTVAL
fetch_iv_le(INTVAL w)
{
ASSERT_ARGS(fetch_iv_le)
#if !PARROT_BIGENDIAN
return w; // No-op on little endian hardware
#else
# if INTVAL_SIZE == 4
return (w << 24) | ((w & 0xff00) << 8) | ((w & 0xff0000) >> 8) | (w >> 24);
# else
# if INTVAL_SIZE == 8
INTVAL r;
r = w << 56;
r |= (w & 0xff00) << 40;
r |= (w & 0xff0000) << 24;
r |= (w & 0xff000000) << 8;
r |= (w & 0xff00000000) >> 8;
r |= (w & 0xff0000000000) >> 24;
r |= (w & 0xff000000000000) >> 40;
r |= (w & 0xff00000000000000) >> 56;
return r;
# endif
# endif
#endif
}
Take a look at this answer: https://stackoverflow.com/a/105339/1461424
If you're using Visual C++ do the following: You include intrin.h and call the following functions:
For 16 bit numbers:
unsigned short _byteswap_ushort(unsigned short value);
For 32 bit numbers:
unsigned long _byteswap_ulong(unsigned long value);
For 64 bit numbers:
unsigned __int64 _byteswap_uint64(unsigned __int64 value);
8 bit numbers (chars) don't need to be converted.
Also these are only defined for unsigned values they work for signed integers as well.
For floats and doubles it's more difficult as with plain integers as these may or not may be in the host machines byte-order. You can get little-endian floats on big-endian machines and vice versa.
Other compilers have similar intrinsics as well.
In GCC for example you can directly call:
uint16_t __builtin_bswap16 (uint16_t x) uint32_t __builtin_bswap32 (uint32_t x) uint64_t __builtin_bswap64 (uint64_t x)
This is my sample. It reverses the endianess of an integer. Notice that C doesn't specify the size of int. It could be 16, 32, 64 ... Using the (uint_16t, uint_32t, uint_64t) instead if you want to ensure the size,
void reverse_endianess(int* number)
{
char byte_arr[8] = {0};
int i;
for (i=0; i < sizeof(int); i++) {
byte_arr[i] = (*number & 0xFF);
*number = *number >> 8;
}
*number = 0;
for (i=0; i < sizeof(int); i++) {
*number |= byte_arr[i];
*number = *number << 8;
}
}
You may want to have a look at this answer, which shows how to convert both ways, also with different byte sizes. This is one way to do it, but more efficient ways are discussed in the thread linked.
uint32_t num = 9;
uint32_t b0,b1,b2,b3,b4,b5,b6,b7;
uint32_t res = 0;
b0 = (num & 0xf) << 28;
b1 = (num & 0xf0) << 24;
b2 = (num & 0xf00) << 20;
b3 = (num & 0xf000) << 16;
b4 = (num & 0xf0000) << 12;
b5 = (num & 0xf00000) << 8;
b6 = (num & 0xf000000) << 4;
b7 = (num & 0xf0000000) << 4;
res = b0 + b1 + b2 + b3 + b4 + b5 + b6 + b7;
printf("%d\n", res);
来源:https://stackoverflow.com/questions/24856846/how-can-i-reorder-the-bytes-of-an-integer