0

I am working on a program that sends TCP header (a simulation of the 3-handshaking). I have a variable and this variable contains the data offset (3 bits), reserved (4 bits), and the 9 flags (9 bits). I am using bitwise operations to set the bits. The question is how can I print each of those bits?

  1. Suppose I store the data offset starting on 2 to 4 (left to right): ex. 0111000000000000

How can I print the 3 bits? Variable:

u_int16_t reserved_ofs_flags;

I found this question similar, but the answer only works for the least significant: How do I print one bit?

phuclv
  • 37,963
  • 15
  • 156
  • 475
Fred
  • 240
  • 2
  • 9
  • 1
    Does this answer your question? [How do you set, clear, and toggle a single bit?](https://stackoverflow.com/questions/47981/how-do-you-set-clear-and-toggle-a-single-bit) – phuclv Apr 09 '22 at 06:21

3 Answers3

5

To extract N bits from an unsigned value starting at position P, counting from 0 for the least significant bit, you can use this expression:

unsigned x = (value >> P) & ((1U << (N - 1) << 1) - 1);

Notes:

  • numbering the bits from left to right is a confusing convention. In software, the preferred numbering method is from the least significant bit (number 0) to the most significant bit (number 15 in your example).
  • If N is a compile time constant, the expression ((1U << (N - 1) << 1) - 1) is evaluated at compile time.
  • The expression assumes that N is at least 1 and at most the number of bits in the unsigned type.
  • A simpler expression ((1U << N) - 1) has undefined behavior if N is the number of bits in the unsigned type.
  • For your example P is 12 and N is 3 so you can write: unsigned x = (value >> 12) & 7;
chqrlie
  • 131,814
  • 10
  • 121
  • 189
4

You can use the bitwise operations to get the bits you want.

For instance:

unsigned int a = 22;           // 10110 in binary
printf("%d\n", a & 1);         // get the bit in the 1's place (0)
printf("%d\n", (a >> 1) & 1);  // get the bit in the 2's place (1)
printf("%d\n", (a >> 2) & 1);  // get the bit in the 4's place (1)
printf("%d\n", (a >> 3) & 1);  // get the bit in the 8's place (0)
printf("%d\n", (a >> 4) & 1);  // get the bit in the 16's place (1)

// 3 in decimal is 11 in binary
printf("%d\n", a & 3);         // get the bits in the 1's and the 2's places
                               // (2 in decimal, 10 in binary)
printf("%d\n", (a >> 1) & 3);  // get the bits in the 2's and the 4's places
                               // (3 in decimal, 11 in binary)
fanfly
  • 406
  • 2
  • 5
2

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

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85