C - Signed and Unsigned integer
Question: I'm delving into C because I need to import ctypes library to python to allow
for keyboard control. I'm trying to learn how the following code works:

    
    
    import ctypes
    import time
    
    SendInput = ctypes.windll.user32.SendInput
    
    # C struct redefinitions 
    PUL = ctypes.POINTER(ctypes.c_ulong)
    class KeyBdInput(ctypes.Structure):
        _fields_ = [("wVk", ctypes.c_ushort),
                    ("wScan", ctypes.c_ushort),
                    ("dwFlags", ctypes.c_ulong),
                    ("time", ctypes.c_ulong),
                    ("dwExtraInfo", PUL)]
    
    class HardwareInput(ctypes.Structure):
        _fields_ = [("uMsg", ctypes.c_ulong),
                    ("wParamL", ctypes.c_short),
                    ("wParamH", ctypes.c_ushort)]
    
    class MouseInput(ctypes.Structure):
        _fields_ = [("dx", ctypes.c_long),
                    ("dy", ctypes.c_long),
                    ("mouseData", ctypes.c_ulong),
                    ("dwFlags", ctypes.c_ulong),
                    ("time",ctypes.c_ulong),
                    ("dwExtraInfo", PUL)]
    
    class Input_I(ctypes.Union):
        _fields_ = [("ki", KeyBdInput),
                     ("mi", MouseInput),
                     ("hi", HardwareInput)]
    
    class Input(ctypes.Structure):
    _fields_ = [("type", ctypes.c_ulong),
                ("ii", Input_I)]
    
    # Actuals Functions
    
    def PressKey(hexKeyCode):
    
        extra = ctypes.c_ulong(0)
        ii_ = Input_I()
        ii_.ki = KeyBdInput( hexKeyCode, 0x48, 0, 0, ctypes.pointer(extra) )
        x = Input( ctypes.c_ulong(1), ii_ )
        SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))
    
    def ReleaseKey(hexKeyCode):
    
        extra = ctypes.c_ulong(0)
        ii_ = Input_I()
        ii_.ki = KeyBdInput( hexKeyCode, 0x48, 0x0002, 0, ctypes.pointer(extra) )
        x = Input( ctypes.c_ulong(1), ii_ )
        SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))
    
    
    def AltTab():
        '''
        Press Alt+Tab and hold Alt key for 2 seconds in order to see the overlay
        '''
    
        PressKey(0x012) #Alt
        PressKey(0x09) #Tab
        ReleaseKey(0x09) #~Tab
    
        time.sleep(2)       
        ReleaseKey(0x012) #~Alt
    
    
    if __name__ =="__main__":
    
        AltTab()
    

The part I'm not understanding is related to signed and unsigned integers:

int has a range of -32768 - 32767

unsigned int has a range of 0 - 65535

I read: "The total range of numbers that can be displayed by a 2 byte number
is 2^16, since you have 16 bits that can represent a number. 2^16 is the same
as 65536, which since we count from 0, is the same as 0 - 65535. This
obviously matches up with the values for an unsigned int, so you can see that
this is how that type operates."

This seems to make sense, but there's one thing I don't understand:

1 byte = 8 bits 2 bytes = 16 bits

so why is a 2 byte number referred to as 2^16 rather than 2^8?


Answer: A 2 byte number has 16 bits (2 x 8 bits). The function that tells you the
highest unsigned number that can be represented by a given number of bits is
`2^n-1`, so for instance 8 bits can represents numbers 0 to 255, 16 bits 0 to
65,535, etc.

The reason for this is simple. Consider the first number that _cannot_ be
represented by (say) 16 bits. That would be 1 with 16 zeros, as that's the
smallest binary number with 17 digits. That's `2^16`. So the largest number
that can be represented that way is `2^16-1`.

Also note that the size of `int` in C will depend on your C compiler. It may
not always be 2 bytes long.

