Reputation: 16988
Can somebody please explain the code for me?
Why 32 bit UINT
?
Why 0xff
?
What are 4 right shifts doing here?
int writeUINT32little(FILE *f, UINT32 i)
{
int rc;
rc = fputc((i & 0xff), f);
if (rc == EOF) return rc;
rc = fputc(((i >> 8) & 0xff), f);
if (rc == EOF) return rc;
rc = fputc(((i >> 16) & 0xff), f);
if (rc == EOF) return rc;
return fputc(((i >> 24) & 0xff), f);
}
Upvotes: 2
Views: 1560
Reputation: 46341
What this function supposedly does is write a 32bit unsigned integer to a file stream in little-endian.
What it really does is, instead of doing one small simple task and getting it right, it tries to do two things together and gets both wrong.
Before I go into why this is wrong, I'll quickly answer the question itself:
UINT32
? Don't know. It's non standard and better be replaced with uint32_t
.0xff
? Used as a mask to keep the lower 8 bits in a larger variable (32 bits)Why this is wrong?
A char
is guaranteed to be at least 8 bits wide, but it could be larger. This means that fputc
will probably write 8 bits per call, but it might write more. What this means is that on a certain architecture where a char is (for example) 16 bits, the function will write 64 bits per call and it won't be little-endian at all. Additionally, the function is very careful checking for errors but doesn't expose the relevant failure information to the caller (how many bytes were written?).
What's a better design?
First, as always, do one small thing and do it right:
uint32_t toLittleEndian(uint32_t v)
{
uint32_t result = v >> 24;
result |= v << 24;
result |= (v >> 8) & 0xf0;
result |= (v << 8) & 0xf00;
return result;
}
Note: this implementation is deliberately verbose. There's an optimisation opportunity using bit rotation operations but C has no native support for that and requires assembly code
This function only converts a big-endian 32 bit value to its equivalent little-endian representation (actually it alternates between the two and could be named switchEndian
).
Next, we need to write the value to a file:
int write(uint32_t v, FILE *f)
{
return fwrite(&v, sizeof(v), 1, f);
}
This will write exactly 32 bits wide integer, and return the number of bytes actually written. Additionally, it's reusable.
Next, we could create a convenience composite function if we want:
int writeLittleEndian(uint32_t v, FILE *f)
{
return write(toLittleEndian(v), f);
}
Upvotes: 1
Reputation: 1623
This takes a 32-bit unsigned integer and outputs it to a file, byte-by-byte with the least significant byte first (i.e. little-endian).
0xff
is the hexadecimal way of writing 255
in decimal, or 11111111
in binary. And &
is the bitwise and
function. Put together, (i & 0xff)
masks out the least significant byte of the integer.
Then the shifts shift the whole thing over 8 bits and the process repeats to print the next byte.
Upvotes: 9