OscarTheMighty
OscarTheMighty

Reputation: 35

Using bitwise operations on char

So I need some help getting this right. I have an assignment like this:

typedef struct {
    char status[1];
} Dish;

The variable status must only have 1 byte: first 3 bits will contain the number of stars the dish has(0-5), 4th bit tells us if the dish is canceled or not and the 5th bit tells us if the dish is ongoing or not. I tried getting the number of stars like this but it doesn't work:

getBit(char data, int bitNumber) {
 return (data & (1 << bitNumber-1)) != 0;
}
int number = 0;
number = getBit(dish.status,0);
number = number << 1 + getBit(dish.status,1);
number = number << 1 + getBit(dish.status,2);

Since status must only have 1 byte I declared it as above char status[1] is it correct? I also tried using binary operations directly on status but it gives me an error saying that I can't use binary operations between a char and an int. My final objective is getting every information in that byte into a separate variable: stars, canceled, ongoing.

Upvotes: 0

Views: 119

Answers (3)

WedaPashi
WedaPashi

Reputation: 3872

As warned about in comments below, kindly note that the behavior of this code is not guaranteed to be consistent across all platforms.
The suggested answers gets the work done. If you don't want to get into bit-shift operations try this:

typedef struct dist {
    union {
        struct {
            uint8_t number_of_stars : 3 ;
            uint8_t cancel_status : 1 ;
            uint8_t ongoing_status :1 ;
            uint8_t reserved : 3;
        };

        uint8_t status;

    };

} DishType;

DishType Dish;

If you check the size of Dish you'll see that it still consumes one byte of memory.

You can set the parameters in a single line such as:

Dish.status = 0x0C; // For example

Or you can set the particular parameter like:

Dish.cancel_status = 1;
Dish.ongoing_status = 0;

And yet, access or print or modify it individually:

printf("\nNo. of starts = %u", Dish.number_of_stars);

Upvotes: 1

Alex B
Alex B

Reputation: 363

With some tweaks your code works:

int getBit(char data, size_t bitNumber) {
 return (data & (1 << (bitNumber - 1))) != 0;
}

int main(void) {

  char status = 3 + (1 << 5);

  for (size_t i = 1; i <= 8; ++i) {
    printf("Bit %d: %d\n", (int) i, getBit(status, i));
  }
  return 0;
}

I used size_t for the bit number (which is unsigned) and fixed the braces around the term.

With bit fields, the code could look like this:

#include <stdio.h>

typedef struct {
  union {
    struct {
      unsigned stars:3;
      char cancelled:1;
      char ongoing:1;
    };
    unsigned char data;
  };
} Dish;

void printDish(Dish dish) {
  printf("Stars = %d, Cancelled = %d, Ongoing = %d\nValue: %d\n", dish.stars, dish.cancelled, dish.ongoing, dish.data);
}

int main(void) {

  Dish dish1;
  dish1.stars = 3;
  dish1.cancelled = 1;
  dish1.ongoing = 1;
  printDish(dish1);

  Dish dish2;
  dish2.stars = 5;
  dish2.cancelled = 0;
  dish2.ongoing = 0;

  printDish(dish2);
  return 0;
}

This will output:

clang version 7.0.0-3~ubuntu0.18.04.1 (tags/RELEASE_700/final)
 clang-7 -pthread -lm -o main main.c
 ./main
Stars = 3, Cancelled = -1, Ongoing = -1
Value: 27
Stars = 5, Cancelled = 0, Ongoing = 0
Value: 37

You can access the 'char' value with .data and everything else with named fields. If you want to cast the file you read (via fread), you probably want to make sure alignment of the struct is correct.

See https://repl.it/repls/SimultaneousIncredibleRoot

Upvotes: 2

Vlad from Moscow
Vlad from Moscow

Reputation: 311108

There is no great sense to declare the data member status as an array with one element

char status[1];

You could declare it like

unsigned char status;

The function can look like it is shown in the demonstrative program below.

#include <stdio.h>
#include <limits.h>

unsigned int getBit( unsigned char data, unsigned int bitNumber ) 
{
    bitNumber %= CHAR_BIT;

    return ( data & (1 << bitNumber ) ) != 0;
}

int main(void) 
{
    for ( int i = 0; i <3; i++ )
    {
        printf( "%d\n", getBit( 6, i ) );
    }       

    return 0;
}

The program output is

0
1
1

On the other hand, as the data member status consists from bit fields then instead of extracting one bit you could use masks for each bit field to extract it.

Another approach is to use bit fields instead of an object of the type unsigned char. Something like

typedef struct {
    unsigned char stars: 3;
    unsigned char cancelled: 1;
    //... 
} Dish;

Upvotes: 2

Related Questions