If I understand your question, and you want to be able to extract the value of the N number of bits (from 1 to sizeof(type) * CHAR_BIT) beginning at position P (from 0 to sizeof(type) * CHAR_BIT - 1) then you can extract that subset of bits with:
/** extract N bits from value starting at position P,
* counting from 0 for the least significant bit
*/
unsigned nbitsatp (unsigned value, unsigned N, unsigned P)
{
/* mask is N 1's bits */
unsigned mask = ~0u >> ((sizeof mask * CHAR_BIT) - N);
return (value >> (P - N + 1)) & mask;
}
(note: for 4-byte unsigned, N ranges from 1 to 32 while P is zero-based ranging from 0 to 31)
Above, the mask begins with all bits 1 (~0u) and then shifts off the total number of bits minus N (leaving N 1's bits). The value is then shifted by P - N + 1 so that you AND the desired number of bits at position P with the corresponding number of 1's bits. Since both are shifted so the values begin at the least significant bit, the result is the value of the N bits at position P.
This avoids having to hardcode the number and individual bit positions for each range of bits you want.
In your example you want to extract the first 3 bits from 0111000000000000 (28672) which would be the 3 bits N == 3 at positon P == 15, the result being 011 (3).
A Short Example
The example below uses unsigned as the type, which so long as it is at least 2-bytes on your hardware will be more than sufficient for uint16_t type.
#include <stdio.h>
#include <limits.h>
/** extract N bits from value starting at position P,
* counting from 0 for the least significant bit
*/
unsigned nbitsatp (unsigned value, unsigned N, unsigned P)
{
/* mask is N 1's bits */
unsigned mask = ~0u >> ((sizeof mask * CHAR_BIT) - N);
return (value >> (P - N + 1)) & mask;
}
int main (void) {
unsigned v, n, p;
fputs ("enter v, n, p : ", stdout); /* prompt for v, n, p */
/* read/validate positive int value */
if (scanf ("%u%u%u", &v, &n, &p) != 3) {
fputs ("error: invalid unsigned integer input.\n", stderr);
return 1;
}
/* output result */
printf ("\nvalue of %u bits at pos %u in %u is : %u\n",
n, p, v, nbitsatp (v, n, p));
}
Example Use/Output
Your specific example of wanting the 3 bits beginning a position 15 of 28672:
$ ./bin/nbitsatp
enter v, n, p : 28672 3 15
value of 3 bits at pos 15 in 28672 is : 3
Or let's take the first 4 bits at position 15, 0111 (7):
$ ./bin/nbitsatp
enter v, n, p : 28672 4 15
value of 4 bits at pos 15 in 28672 is : 7
Or the first 5-bits beginning at position 15:
$ ./bin/nbitsatp
enter v, n, p : 28672 5 15
value of 5 bits at pos 15 in 28672 is : 14
Or what about the value for the 9-flags (9 bits, at position 8) which will be all zero in your example:
$ ./bin/nbitsatp
enter v, n, p : 28672 9 8
value of 9 bits at pos 8 in 28672 is : 0
Using Pre-Defined Macros To Retrieve Wanted Bits
A convenient way to use the nbitsatp() function to retrieve the bits you are interested in is to #define a macro for each group of bits you want to obtain. For example to get the 3-bits for the data offset, the reserved 4-bits and the 9-bit set of flags, you could define three macros that set the number of bits and position allowing you to simply pass the TCP Header value as an argument, e.g.
/* macros for 3-bit offset, 4-bit reserved, 9-bit flags */
#define HDR_OFFSET(TCPHDRVAL) nbitsatp ((TCPHDRVAL), 3, 15)
#define HDR_RESERVED(TCPHDRVAL) nbitsatp ((TCPHDRVAL), 4, 12)
#define HDR_FLAGS(TCPHDRVAL) nbitsatp ((TCPHDRVAL), 9, 8)
To obtain the bits you want, just call the macro passing the TCP header value as an argument, e.g.
int main (void) {
unsigned v;
fputs ("enter TCP hdr value : ", stdout); /* prompt TCP HDR VAL */
/* read/validate TCP header value */
if (scanf ("%u", &v) != 1) {
fputs ("error: invalid unsigned integer input.\n", stderr);
return 1;
}
/* output result */
printf ("\n data offset bits : %u\n"
" reserved bits : %u\n"
" flag bits : %u\n",
HDR_OFFSET (v), HDR_RESERVED (v), HDR_FLAGS (v));
}
Output
$ /bin/nbitsatp_macro
enter TCP hdr value : 28672
data offset bits : 3
reserved bits : 8
flag bits : 0
If wanted, you can output the padded binary representation for 3 (011), 8 (1000) and then the flags (000000000) in a trivial manner. See binprnpad() function in this answer