HIWORD and LOWORD macros

This is a discussion on HIWORD and LOWORD macros within the C Programming forums, part of the General Programming Boards category; I have seen the HIWORD and LOWORD in some winapi header file. As their name suggests they select the higher ...

  1. #1
    Registered User
    Join Date
    Nov 2008
    Posts
    5

    Post HIWORD and LOWORD macros

    I have seen the HIWORD and LOWORD in some winapi header file. As their name suggests they select the higher or lower WORD (16bit) from a DWORD(32bit). Whats the function of the '& 0xFFFF' in the definition of the HIWORD macro? I know what it does, but for me, it seems pointless. The right shift leaves 0s in the higher 16bits anyway, so whats the point in masking them out?

    Simple test program:
    Code:
    #include <stdio.h>
    
    typedef unsigned long DWORD;
    typedef unsigned short WORD;
    #define LOWORD(a) ((WORD)(a))
    #define HIWORD(a) ((WORD)(((DWORD)(a) >> 16) & 0xFFFF))
    
    int main()
    {
    	DWORD d=0xABCD1234;
    
    	printf("Original value:\n");
    	printf("       d  == %X\n",d);
    
    	printf("\nMacros:\n");
    	printf("LOWORD(d) == %X\n", LOWORD(d));
    	printf("HIWORD(d) == %X\n", HIWORD(d));
    
    	printf("\nSimple hiword:\n");
    	printf("  d >> 16 == %X\n", d >> 16);
    	
    	
    	return 0;
    }

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,185
    Because the right shift may or may not leave zeroes. Try shifting -1 >> 16 and see what happens.

  3. #3
    Registered User
    Join Date
    Nov 2008
    Posts
    5

    ...

    As I understand right shift does sign extension, thus signed negative numbers leave 1s, signed positive and unsigned numbers leave 0s in the upper bits.

    Some test code:
    Code:
    #include <stdio.h>
    
    typedef unsigned long DWORD;
    typedef unsigned short WORD;
    #define LOWORD(a) ((WORD)(a))
    #define HIWORD(a) ((WORD)(((DWORD)(a) >> 16) & 0xFFFF))
    
    int main()
    {
    	int a=-1; // signed numbers are stored in two's complement, so this equals 0xFFFFFFFF
    	printf("a >> 16            == &#37;X\n",a >> 16); // FFFFFFFF
    	printf("(DWORD)(a) >> 16   == %X\n",(DWORD)(a) >> 16); //FFFF
    	printf("\n");
    	
    	
    	a=0xABCD1234;
    	//upper bits are sign extended -> int a : signed
    	printf("a >> 16            == %X\n",a >> 16); // FFFFABCD 
    	
    	//no sign extension -> (DWORD) is an unsigned cast
    	printf("(DWORD)(a) >> 16   == %X\n",(DWORD)(a) >> 16); //ABCD
    	return 0;
    }
    For me it seems that there is still no need for a mask, because there is an unsigned cast in the macro definition: (DWORD)(a). So, why the need for the mask '& 0xFFFF' when we already have an unsigned cast?
    Last edited by kalevi; 12-05-2008 at 09:22 PM. Reason: fix, clarification, formatting

  4. #4
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,185
    I didn't notice the cast. So someone was probably just being paranoid.

  5. #5
    Registered User
    Join Date
    Nov 2008
    Posts
    5
    Its good to know I don't miss anything there. Thanks for the help!

  6. #6
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Posts
    23,001
    Or they wanted to be sure that they could stuff that into a WORD without warnings.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  7. #7
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Also, if you use the and at the end of such an operation, most optimizing compilers will "know" if it actually needs to perform the and operation or not - and thus it's not actually causing a problem to perform the and when it's not needed. If you then port the code to a machine which can't do this without using an and, it will still work.

    Just because something works with one or two compilers on one or two types of machine, doesn't necessarily mean that it will work in ALL compilers or ALL processor architectures. Belts and braces.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  8. #8
    Registered User
    Join Date
    Nov 2008
    Posts
    5
    So, the AND part makes the macro more robust. By the way, -out of curiosity- do you know cases (arch/os/compiler) where the code without the '& 0xffff' would not work?

    Furthermore, how should I code in a platform/compiler independent manner? Any guide, book or tutorial on that?

    Thanks for the useful comments!

  9. #9
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by kalevi View Post
    So, the AND part makes the macro more robust. By the way, -out of curiosity- do you know cases (arch/os/compiler) where the code without the '& 0xffff' would not work?

    Furthermore, how should I code in a platform/compiler independent manner? Any guide, book or tutorial on that?

    Thanks for the useful comments!
    The code as it is seems right. I don't see any reason to change it. I've not worked on any system where the cast to a smaller time would perform an operation corresponding to the AND (either explicitly as an and, or some other operation that has the same effect). However, the C standard explicitly says that the result, if the receiving type on the left can not fit the entire result from the right, then it is undefined. It may be obscure and strange machines that this statement in the standard is there for - but it wouldn't be in the standard if the standards committee didn't think it was at least likely, if not true, that such machines would at some point in time exist (past, present or future). All the standards definitions are thought out for some reason - they don't just say "Oh, well, it's too hard to define this, let's just make it undefined". Rather someone comes up with a definition, and then the representative of some company that produces a compiler, or a processor manufacturer says "Well, that would break on our design, can we make it undefined under <some> circumstances". This ensures that a compiler CAN be produced for all architectures (at least the ones that are relevant).

    Of course, if you KNOW that the compiler will do the right thing without the and, then you may omit the AND operation. But you will also have to trust the compiler to not "break" this behaviour (and the standard does allow it to break!) in the future.

    But as I said earlier, I'm convinced that this type of operation can be optimized away by most modern compilers in most cases where it would occur - because it's not an unusual pattern, so the compiler would have a special case to deal with this sort of thing.

    We sometimes get a bit too caught up in standards and undefined behaviour here, but having worked with many differnet compilers, I do KNOW that there are situations where the compiler implementor will "choose" between different solutions that are all within the standard - and if your work involves combining the result of two compilers, then it DOES sometimes get you into trouble when these things are cropping up. Not to mention that sometimes the choice changes from one compiler version to another.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Hiword, Lowword......please Help!!!!!!
    By incognito in forum Windows Programming
    Replies: 7
    Last Post: 03-25-2002, 12:37 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21