Reputation: 309
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.
Upvotes: 0
Views: 7958
Reputation: 84521
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.)
Upvotes: 1
Reputation: 37214
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;
}
Upvotes: 1
Reputation: 5220
After each 8 bits
for(int b=7;b>=0;b--)
{
printf("%d", (chararray[j] & (1<<b)) ? 1 : 0);
}
printf("\n");
Upvotes: 1