0

How do I set individuals bits of an unsigned char array in c? If I have

 unsigned char chararray[5];
 memset(chararray, ~0, 5);

I am setting all the bits to 0. I then need to set certain bits to 1. I know there are 8 bits in each element of this array so if I was to set the 3rd bit in the second element I believe I would need to do something like

chararray[1] |= (1 << 3);

I need to set all bits of the array to 1 to match another array. For example I have a 40 element integer array containing 1 and -1. If the number in the integer array at index i is 1 I want to set the bit in the character array to 1 and if not leave the bit at 0.

Here is what I have so far:

unsigned char chararray[5];
memset(array, ~0, 5);
int intarray[40]; //filled with random 1's and -1's
int j = 0;
for (int i = 0; i < 40; i++)
{
    if (intarray[i] == 1)
    {
        if(j == 0)
            chararray[0] |= (1 << i);
        else
            chararray[j] |= (1 << (i%8));
    }
    if(i % 8 == 0 && i > 0)
        j++;
}

I believe this is correct but how would I display each bit in order so I can see if they have been set correctly?

Thanks for your help.

2
  • 3
    1) ~0 will not set the values to all-zero, but all one. 2) Don't use magic numbers. If you mean the size of the array, use the sizeof the array! Be carful when bitshifting signed integers (you very well use them!). Commented May 1, 2016 at 21:45
  • memset(array, ~0, 5); sets all the bits to 1 (probably). To set to 0 , use 0 instead of ~0. Or preferably write unsigned char chararray[5] = { 0 }; Commented May 1, 2016 at 21:58

3 Answers 3

2

If I understand the two parts to your question (1) confirm your bit setting for 5 - unsigned char values from a 40-element integer array of -1 & 1 values; and (2) output the binary representation for each of the 5 - unsigned char values, then that can be accomplished a number of ways. In order to set a bit in each unsigned char valued, you simply bitwise OR the current value with 1 left-shifted the appropriate amount to set the bit at issue. For example to set the 3rd-bit in unsigned char v = 0;, you simply set v |= 1<<2;

The several different approaches to set the bit for all 5 unsigned char elements could be done with a nested loop brute force approach:

/* brute force nested loop approach */
for (i = 0; i < szarr; i++)
    for (j = 0; j < CHAR_BIT; j++)
        if (buf[i * CHAR_BIT + j] == 1) arr[i] |= (1 << j);

(where szarr is sizeof *arr for your char arr[5] = {0}; array (or just 5, and where CHAR_BIT (limits.h) defines the number of bits in a char (generally 8))

An unrolled approach which has benefits in efficiency (though most current compilers will attempt an unrolled solution with aggressive optimization). It is better illustrative of the process taking place:

/* unrolled loop setting all bits in each unsigned char per loop */
for (i = 0; i < szbuf; i+=8) {
    if (buf[ i ] == 1) arr[i/8] |= 1;
    if (buf[i+1] == 1) arr[i/8] |= (1 << 1);
    if (buf[i+2] == 1) arr[i/8] |= (1 << 2);
    if (buf[i+3] == 1) arr[i/8] |= (1 << 3);
    if (buf[i+4] == 1) arr[i/8] |= (1 << 4);
    if (buf[i+5] == 1) arr[i/8] |= (1 << 5);
    if (buf[i+6] == 1) arr[i/8] |= (1 << 6);
    if (buf[i+7] == 1) arr[i/8] |= (1 << 7);
}

And finally how can you easily look at the binary representation of each of your unsigned values to confirm your logic. There are as many variations in how you can accomplish this as with anything else. For general testing, I find a simple function that returns a char * representation of the binary string, padded to a desired length, as, or more, useful than just about any of the other methods as it allow you to retain normal formatting control within a single printf, etc..

While not 100% necessary with unsigned values, as most hardware agrees that 4-bytes make up an int and unsigned. However, since there can be variations, it is a good idea to either use exact types or use simple preprocessor directives to determine the number of BITS_PER_LONG, etc.. With a generic binary print routine that will handle padding long unsigned values, a simple test is helpful between 32/64-bit machines. e.g.:

/* BITS_PER_LONG */
#if defined(__LP64__) || defined(_LP64)
# define BITS_PER_LONG 64
#else
# define BITS_PER_LONG 32
#endif
...
/** returns pointer to binary representation of 'v' zero padded to 'sz'.
 *  returns pointer to string contianing binary representation of
 *  unsigned 64-bit (or less ) value zero padded to 'sz' digits.
 */
char *binpad (const unsigned long v, const size_t sz)
{
    static char s[BITS_PER_LONG + 1] = {0};
    char *p = s + BITS_PER_LONG;
    register size_t i;

    for (i = 0; i < sz; i++)
        *--p = (v>>i & 1) ? '1' : '0';

    return p;
}

Now putting all the pieces together and making up a 40-element array of values that are -1 or 1 for testing purposes, you could do something like the following, and using the simple compiler definitions NESTED to signify to build with the brute force nested loop version, or with the unrolled version if no definitions are given:

#include <stdio.h>

/* CHAR_BIT */
#ifndef CHAR_BIT
# define CHAR_BIT  8
#endif

/* BITS_PER_LONG */
#if defined(__LP64__) || defined(_LP64)
# define BITS_PER_LONG 64
#else
# define BITS_PER_LONG 32
#endif

char *binpad (const unsigned long v, const size_t sz);

int main (void){

    unsigned char buf[] = { -1, -1,  1, -1, -1, -1,  1,  1,
                            -1,  1, -1, -1, -1,  1,  1, -1,
                             1, -1, -1, -1,  1,  1, -1, -1,
                            -1, -1, -1,  1,  1, -1, -1, -1,
                            -1, -1,  1,  1, -1, -1, -1,  1 };
    unsigned char arr[5] = {0};
    unsigned i, szarr = (unsigned) sizeof arr;

#ifdef NESTED
    unsigned j;
    /* brute force nested loop approach */
    for (i = 0; i < szarr; i++)
        for (j = 0; j < CHAR_BIT; j++)
            if (buf[i * CHAR_BIT + j] == 1) arr[i] |= (1 << j);
#else
    unsigned szbuf = (unsigned) sizeof buf;
    /* unrolled loop setting all bits in each unsigned char per loop */
    for (i = 0; i < szbuf; i+=8) {
        if (buf[ i ] == 1) arr[i/8] |= 1;
        if (buf[i+1] == 1) arr[i/8] |= (1 << 1);
        if (buf[i+2] == 1) arr[i/8] |= (1 << 2);
        if (buf[i+3] == 1) arr[i/8] |= (1 << 3);
        if (buf[i+4] == 1) arr[i/8] |= (1 << 4);
        if (buf[i+5] == 1) arr[i/8] |= (1 << 5);
        if (buf[i+6] == 1) arr[i/8] |= (1 << 6);
        if (buf[i+7] == 1) arr[i/8] |= (1 << 7);
    }
#endif    

    for (i = 0; i < szarr; i++) /* validate the bit settings */
        printf (" arr[%2u] : %3u  (%s)\n",
                i, arr[i], binpad (arr[i], CHAR_BIT));

    return 0;
}

/** returns pointer to binary representation of 'v' zero padded to 'sz'.
 *  returns pointer to string contianing binary representation of
 *  unsigned 64-bit (or less ) value zero padded to 'sz' digits.
 */
char *binpad (const unsigned long v, const size_t sz)
{
    static char s[BITS_PER_LONG + 1] = {0};
    char *p = s + BITS_PER_LONG;
    register size_t i;

    for (i = 0; i < sz; i++)
        *--p = (v>>i & 1) ? '1' : '0';

    return p;
}

Example Use/Output

The output values are for each of your 5 unsigned char array elements showing both the decimal value created from the interpretations of -1's and 1's as well as the binary representation for that number padded to 8-chars:

$ ./bin/array_uc_bits
 arr[ 0] : 196  (11000100)
 arr[ 1] :  98  (01100010)
 arr[ 2] :  49  (00110001)
 arr[ 3] :  24  (00011000)
 arr[ 4] : 140  (10001100)

Look over all the answers and all the approaches and let me know if you have additional questions.

Note: if you are unfamiliar with how to set a label definition like NESTED at compile time, all you need do is pass it as an option preceded with -D, e.g. -DNESTED passed on the command line will trigger compiling the nested brute force code., e.g.

$ gcc -Wall -Wextra -Ofast -DNESTED -o bin/array_uc_bits_nest array_uc_bits.c

is all that is required. (of course adjust the input and output names to your liking.)

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the detailed response!
Sure, glad to help. There are always several ways to approach each problem in C. There is a little bit to be learned from each. Good luck with your coding.
1

After each 8 bits

for(int b=7;b>=0;b--)
{
   printf("%d", (chararray[j] & (1<<b)) ? 1 : 0);
}
printf("\n");

Comments

1

Simple version:

for (int i = 0; i < 40; i++) {
    if (intarray[i] == 1) {
        chararray[i/8] |= (1 << (i%8) );
    }
}

It should be possible (using various tricks, and relying on a little "implementation defined" behaviour) to create a faster version that does 8 integers at a time (and avoids a lot of branches, and avoids the need to pre-fill the chars with zero); like:

for (int i = 0; i < 40; i += 8) {
    temp = ((unsigned int)intarray[i]) >> 31;
    temp |= (((unsigned int)intarray[i+1]) >> 31) << 1;
    temp |= (((unsigned int)intarray[i+2]) >> 31) << 2;
    temp |= (((unsigned int)intarray[i+3]) >> 31) << 3;
    temp |= (((unsigned int)intarray[i+4]) >> 31) << 4;
    temp |= (((unsigned int)intarray[i+5]) >> 31) << 5;
    temp |= (((unsigned int)intarray[i+6]) >> 31) << 6;
    temp |= (((unsigned int)intarray[i+7]) >> 31) << 7;
    chararray[i/8] = ~temp;
}

It would be more fun if your integers contained 0 and 1 (instead of -1 and 1); because then you'd be able to get away with something like this:

for (int i = 0; i < 40; i += 8) {
    temp = intarray[i];
    temp |= intarray[i+1] << 1;
    temp |= intarray[i+2] << 2;
    temp |= intarray[i+3] << 3;
    temp |= intarray[i+4] << 4;
    temp |= intarray[i+5] << 5;
    temp |= intarray[i+6] << 6;
    temp |= intarray[i+7] << 7;
    chararray[i/8] = temp;
}

1 Comment

For the simple version, doing this: for (int i = 0; i < 40; i++) chararray[i/8] |= ((intarray[i] == 1) << (i%8)); eliminates the extra conditional branch in the loop and reduces the function size by 16 bytes [on x86]. Didn't bench it, however, so it may be a moot point.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.