dr******@gmail.com writes:
I'm not that expert at C but I'm trying to understand some code that
does extensive use of the following two preprocessor macros:
#define WORD(ptr) (((*(octet *)(ptr))<<8)|(*(octet *)((ptr)+1)))
#define DWORD(ptr) (((*(octet *)(ptr))<<24)|(*(octet *)((ptr)+1)<<16)|\
(*(octet *)((ptr)+2)<<8)|(*(octet *)((ptr)+3)))
I'm lost with all that shifting and indirection :-(
Could some kind soul explain what these macros are doing?
Presumably "octet" is a typedef for an 8-bit type, probably unsigned
char. (The code probably won't work on a system with CHAR_BIT!=8.)
WORD retrieves a 16-bit unsigned integer value, stored high-order byte
first (big-endian, also known as network order), pointed to by ptr.
DWORD retrieves a 32-bit unsigned integer value, stored
high-order-byte first (big-endian), pointed to by ptr.
Let's look at the definition of WORD, working from the inside out.
(ptr) is a pointer.
(octet *)(ptr) is ptr converted to a pointer-to-octet.
(*(octet *)(ptr)) dereferences the converted pointer, yielding an
octet value (a number in the range 0..255). Let's call this
HIGH_OCTET.
(ptr)+1 is points one element past the object pointed to by ptr. For
this to work properly, the actual argument passed to WORD had better
be of some character pointer type. If it's a void*, you can't legally
perform arithmetic on it. If it's an int*, for example, adding one
advances too far. If you wanted to allow WORD() to apply to arbitrary
pointer-to-void or pointer-to-object types, you'd want to add 1
*after* converting to (octet*). We'll assume that the argument is a
pointer-to-character.
(*(octet *)((ptr)+1)) retrieves the octet value from just after the
location pointed to by ptr. Let's call this LOW_OCTET.
The whole expression then becomes (HIGH_OCTET<<8|LOW_OCTET).
So, if ptr is of type unsigned char*, and *ptr==0x12, and
*(ptr+1)==0x34, then WORD(ptr) will be 0x1234.
Similarly, if ptr points to a sequence of unsigned bytes with the
values 0x12, 0x34, 0x56, and 0x78, DWORD(ptr) will be 0x12345678.
Note that if type int is 16 bits, the default integer promotions may
cause the DWORD() macro to fail, since there's nothing to indicate
that any of the operands are 32 bits. This could be corrected by
defining a 32-bit unsigned integer type (or using uint32_t) and
inserting several casts. But it may not be a problem if the code is
not required to be portable to such systems.
Both macros could be simplified to convert the pointer to a pointer to
the appropriate 16-bit or 32-bit type and dereference it, but *only*
if the machine uses a big-endian representation *and* the byte arrays
are appropriately aligned.
I suspect the macros are used in networking code, to extract 16-bit
and 32-bit unsigned integer values from network-order byte streams.
--
Keith Thompson (The_Other_Keith)
ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.