How to Implement a Bitset in C [closed] - c

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 6 years ago.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Improve this question
The below code, which creates and uses a bitset, is from the following tutorial "Intro to Bit Vectors". I am rewriting this code to try to learn and understand more about C structs and pointers.
#include <stdio.h>
#include <stdlib.h>
#define WORDSIZE 32
#define BITS_WORDSIZE 5
#define MASK 0x1f
// Create a bitset
int initbv(int **bv, int val) {
*bv = calloc(val/WORDSIZE + 1, sizeof(int));
return *bv != NULL;
}
// Place int 'i' in the biset
void set(int bv[], int i) {
bv[i>>BITS_WORDSIZE] |= (1 << (i & MASK));
}
// Return true if integer 'i' is a member of the bitset
int member(int bv[], int i) {
int boolean = bv[i>>BITS_WORDSIZE] & (1 << (i & MASK));
return boolean;
}
int main() {
int *bv, i;
int s1[] = {32, 5, 0};
int s2[] = {32, 4, 5, 0};
initbv(&bv, 32);
// Fill bitset with s1
for(i = 0; s1[i]; i++) {
set(bv, s1[i]);
}
// Print intersection of bitset (s1) and s2
for(i = 0; s2[i]; i++) {
if(member(bv, s2[i])) {
printf("%d\n", s2[i]);
}
}
free(bv);
return 0;
}
Below, is what I have rewritten to make use of structs.
#include <stdio.h>
#include <stdlib.h>
#define WORDSIZE 32
#define BITS_WS 5
#define MASK 0x1f
struct bitset {
int *bv;
};
/* Create bitset that can hold 'size' items */
struct bitset * bitset_new(int size) {
struct bitset * set = malloc(sizeof(struct bitset));
set->bv = calloc(size/WORDSIZE + 1, sizeof(int));
return set;
}
/* Add an item to a bitset */
int bitset_add(struct bitset * this, int item) {
return this->bv[item>>BITS_WS] |= (1 << (item & MASK));
}
/* Check if an item is in the bitset */
int bitset_lookup(struct bitset * this, int item) {
int boolean = this->bv[item>>BITS_WS] & (1 << (item & MASK));
printf("%d\n", boolean);
return boolean;
}
int main() {
struct bitset * test = bitset_new(32);
int num = 5;
bitset_add(test, num);
printf("%d\n", bitset_lookup(test, num));
return 0;
}
What I have rewritten is not working as expected. To test the implementation, in main() I try a bitset_lookup and expect a return value of 0 or 1 but I get a value of 32. I believe it must be something to do with my use of pointers, although I cannot see what I am doing wrong.
Any help appreciated!

That is not a tutorial, it is misleading examples at best.
First of all, use an unsigned type. I recommend unsigned long (for various reasons, none of them critical). The <limits.h> header file defines constant CHAR_BIT, and the number of bits you can use in any unsigned integer type is always CHAR_BIT * sizeof (unsigned_type).
Second, you can make the bit map (or ordered bit set) dynamically resizable, by adding the size information to the structure.
The above boils down to
#include <stdlib.h>
#include <limits.h>
#define ULONG_BITS (CHAR_BIT * sizeof (unsigned long))
typedef struct {
size_t ulongs;
unsigned long *ulong;
} bitset;
#define BITSET_INIT { 0, NULL }
void bitset_init(bitset *bset)
{
if (bset) {
bset->ulongs = 0;
bset->ulong = NULL;
}
}
void bitset_free(bitset *bset)
{
if (bset) {
free(bset->ulong);
bset->ulongs = 0;
bset->ulong = NULL;
}
}
/* Returns: 0 if successfully set
-1 if bs is NULL
-2 if out of memory. */
int bitset_set(bitset *bset, const size_t bit)
{
if (bset) {
const size_t i = bit / ULONG_BITS;
/* Need to grow the bitset? */
if (i >= bset->ulongs) {
const size_t ulongs = i + 1; /* Use better strategy! */
unsigned long *ulong;
size_t n = bset->ulongs;
ulong = realloc(bset->ulong, ulongs * sizeof bset->ulong[0]);
if (!ulong)
return -2;
/* Update the structure to reflect the changes */
bset->ulongs = ulongs;
bset->ulong = ulong;
/* Clear the newly acquired part of the ulong array */
while (n < ulongs)
ulong[n++] = 0UL;
}
bset->ulong[i] |= 1UL << (bit % ULONG_BITS);
return 0;
} else
return -1;
}
/* Returns: 0 if SET
1 if UNSET
-1 if outside the bitset */
int bitset_get(bitset *bset, const size_t bit)
{
if (bset) {
const size_t i = bit / ULONG_BITS;
if (i >= bset->ulongs)
return -1;
return !(bset->ulong[i] & (1UL << (bit % ULONG_BITS)));
} else
return -1;
}
In a bitset structure, the ulong is a dynamically allocated array of ulongs unsigned longs. Thus, it stores ulongs * ULONG_BITS bits.
BITSET_INIT is a preprocessor macro you can use to initialize an empty bitset. If you cannot or do not want to use it, you can use bitset_init() to initialize a bitset. The two are equivalent.
bitset_free() releases the dynamic memory allocated for the bitset. After the call, the bit set is gone, and the variable used is re-initialized. (Note that it is perfectly okay to call bitset_free() on an un-used but initialized bit set, because calling free(NULL) is perfectly safe and does nothing.)
Because the OS/kernel will automatically release all memory used by a program (except for certain types of shared memory segments), it is not necessary to call bitset_free() just before a program exits. But, if you use bit sets as part of some algorithm, it is obviously a good practice to release the memory no longer needed, so that the application can potentially run indefinitely without "leaking" (wasting) memory.
bitset_set() automatically grows the bit set when necessary, but only to as large as is needed. This is not necessarily a good reallocation policy: malloc()/realloc() etc. calls are relatively slow, and if you happen to call bitset_set() in increasing order (by increasing bit number), you end up calling realloc() for every ULONG_BITS. Instead, it is often a good idea to adjust the new size (ulongs) upwards -- the exact formula you use for this is your reallocation policy --, but suggesting a good policy would require practical testing with practical programs. The shown one works, and is quite robust, but may be a bit slow in some situations. (You'd need to use at least tens of thousands of bits, though.)
The bitset_get() function return value is funky, because I wanted the function to return a similar value for both "unset" and "outside the bit set", because the two are logically similar. (That is, I consider the bit set, a set of set bits; in which case it is logical to think of all bits outside the set as unset.)
A much more traditional definition is
int bitset_get(bitset *bset, const size_t bit)
{
if (bset) {
const size_t i = bit / ULONG_BITS;
if (i >= bset->ulongs)
return 0;
return !!(bset->ulong[i] & (1UL << (bit % ULONG_BITS)));
} else
return 0;
}
which returns 1 only for bits set, and 0 for bits outside the set.
Note the !!. It is just two not operators, nothing too strange; making it a not-not operator. !!x is 0 if x is zero, and 1 if x is nonzero.
(A single not operator, !x, yields 1 if x is zero, and 0 if x is nonzero. Applying not twice yields the not-not I explained above.)
To use the above, try e.g.
int main(void)
{
bitset train = BITSET_INIT;
printf("bitset_get(&train, 5) = %d\n", bitset_get(&train, 5));
if (bitset_set(&train, 5)) {
printf("Oops; we ran out of memory.\n");
return EXIT_FAILURE;
} else
printf("Called bitset_set(&train, 5) successfully\n");
printf("bitset_get(&train, 5) = %d\n");
bitset_free(&train);
return EXIT_SUCCESS;
}
Because we do not make any assumptions about the hardware or system we are running (unless I goofed somewhere; if you notice I did, let me know in the comments so I can fix my goof!), and only stuff that the C standard says we can rely on, this should work on anything you can compile the code with a standards-compliant compiler. Windows, Linux, BSDs, old Unix, macOS, and others.
With some changes, it can be made to work on microcontrollers, even. I'm not sure if all development libraries have realloc(); even malloc() might not be available. Aside from that, on things like 32-bit ARMs this should work as-is just fine; on 8-bit AVRs and such it would be a good idea to use unsigned char and CHAR_BIT, instead, since they tend to emulate the larger types rather than supporting them in hardware. (The code above would work, but be slower than necessary.)

The returned value from bitset_lookup should be treated as a binary truth value (i.e. yes on no), not a numerical value. If it's zero, the bit is not set; if it's not zero, the bit is set. Really that function should return boolean != 0, which would force it to a value of zero or one, a true boolean, not the current zero or anything (well actually (1 << (item & MASK))). C/C++ not having a true boolean can cause this sort of confusion.

Related

What really is alignment on a longword boundary in the source code of memchr?

I've tried to understand and rewrite the memchr function but I found something strange at the beginning of the code.
We can read that:
#include "libc.h"
#include <unistd.h>
void *my_memchr(void const *s, int c_in, size_t n)
{
unsigned const char *char_ptr;
unsigned char c;
/*
** t_longword is a typedef for unsigned long int **
*/
t_longword *longword_ptr;
t_longword magic;
t_longword mega_c;
c = (unsigned char)c_in;
for (char_ptr = (unsigned const char*)s; n > 0
&& (size_t)char_ptr % sizeof(t_longword) != 0; --n, ++char_ptr)
{
if (*char_ptr == c)
return ((void*)char_ptr);
}
longword_ptr = (t_longword*)char_ptr;
print_bits(*longword_ptr);
magic = 0x101010101010100;
mega_c = c | (c << 8);
mega_c |= mega_c << 16;
mega_c |= mega_c << 32;
/*
** I didn't finish to rewrite the entire function**
*/
return (NULL);
}
I was wondering why the first loop is mandatory ? I've already tried without in the function strlen and I've got some bugs from time to time but I don't know why.
The optimized part of memchr() requires that it work with a pointer that is four byte aligned. However, there is no requirement that s as passed to the function is aligned in that way.
The purpose of that first loop is to advance s if necessary just far enough that it is correctly aligned for the optimized portion. The loop is as complex as it is because it has to deal with two edge cases:
The character being searched for is in the first non-aligned few bytes, and
The case of the non-aligned starting area being so small that you reach the end of the buffer before getting the pointer aligned.

6-bit binary counter in C (compiles, but then Windows encounters error) [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I am extremely new to C and trying to make a 6-bit binary counter, where each return has all 6 digits listed (i.e 000000, 000001,...). Currently, my solution compiles but does not execute once compiled (I get a warning that says something to the effect of "A problem caused Windows to stop working" and then no output is displayed). If anyone could help figure out why this happens, or suggest a better way to do this since I know my approach is extremely convoluted, I'd appreciate the help!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
long * convert(long dec){
if(dec == 0){
return 0;
}else{
return(long *)(dec % 2 + (10 * *convert(dec / 2)));
}
}
char* long_enough(char* num){
char* have_one = "0000";
char* have_two = "0000";
char* have_three = "000";
char* have_four = "00";
char* have_five = "0";
if(strlen(num) == 2){
strcat(have_one, num);
}else if(strlen(num) == 3){
strcat(have_two, num);
}else if(strlen(num) == 4){
strcat(have_three, num);
}else if(strlen(num) == 5){
strcat(have_four, num);
}else if(strlen(num) == 6){
strcat(have_five, num);
}
}
char main(){
int i;
int count = -1;
printf("\n");
for(i = 0; i < 5; i++){
count++;
long* binNum = (long *)(convert(count));
char* new;
char done = sprintf(new, "%d", binNum);
long_enough((char *)(intptr_t)done);
printf("%s\n", long_enough((char *)(intptr_t)done));
}
}
I think it has to do with your handling of pointers. #Pete Becker suggestion should get get you started but what also jumped out at me was this line:
return(long *)(dec % 2 + (10 * *convert(dec / 2)));
Here you are multiplying by the raw memory of the convert function result. If your intent is to raise to a power, note that there is no exponent operator in C. Do raise to a power, you'll need to #include <math.h> and use the pow(x,e) function.
You'll definitely want to read up on the pointer and value semantics in C. I'd recommend the book The C Programming Language by Brian W. Kernighan & Dennis M. Ritchie (the creators of the language). It is concise and will likely get you further, faster than a lot of the other books out there.
Are you using a long* to point nowhere?
It makes no sense. A pointer is meant to point to a memmory region and this memory region must have been allocated (either stack or heap).
It makes no sense that you are multiplying a binary number by 10 either.
You need to clarify your thought.
When you say you are making a "binary counter", think first about what it means. Given the code you posted, it looks like you should split your problem in two parts:
Count in binary.
Show the value in a human readable manner.
Once you've splitted the problem in two, I'll help you with one and let the other on your own.
Convert a number into binary:
Well, computer numbers are already binary by construction. Let's suppose you need, for any didactic reason, to reinvent the wheel in such a way that you can address individual values.
You have basically two options: use an array or use a bit-mask.
By using an array you'll waste more memory but printing the result will be easier.
By using a bit-mask you basically will have to allocate a single integer (or even char since you just need 6 bits) and shift it left while testing the original number.
// Example using bit-masks / bit-shifts into an uchar.
unsigned char to_bin6( unsigned int number )
{
unsigned char bin6 = 0;
// Left align and Clear out extra bits since we only care about the lowest 6.
number <<= ( 8 * sizeof( unsigned int ) ) - 6;
for ( int count = 6; count; --count )
bin6 <<= number;
return bin6;
}
Now with arrays.
// Example using char array.
// Array needs to have an additional element for the EoS (end of string) marker.
void to_bin6( unsigned int number, unsigned char bin6[ 7 ] )
{
// Fills the output buffer in the same direction you'd expect to read.
for ( int count = 5; count >= 0; --count )
bin6[ count ] = ( number & ( 1 << count ) ) ?
'1' : '0'; // Feeds character '1' or '0' according to bit value.
bin6[ 6 ] = '\0'; // EoS: Note this is NOT the same as '0'.
}
Show a number as binary for human reading:
That's the part I'll leave for you.
Hint: using array it is very easy.
Tell me if you need more.

Fastest way to check mass data if null in C? [duplicate]

This question already has answers here:
Faster approach to checking for an all-zero buffer in C?
(19 answers)
Closed 7 years ago.
I have a mass of data, maybe 4MB. Now want to check if all bits in it are 0.
Eg:
Here is the data:
void* data = malloc(4*1024*1024);
memset(data, 0, 4*1024*1024);
Check if all bits in it are 0. Here is my solution which is not fast enough:
int dataisnull(char* data, int length)
{
int i = 0;
while(i<length){
if (data[i]) return 0;
i++;
}
return 1;
}
This code might have some things to improve in performance. For example, in 32/64 bits machine, checking 4/8 bytes at a time may be faster.
So I wonder what is the fastest way to do it?
You can handle multiple bytes at a time and unroll the loop:
int dataisnull(const void *data, size_t length) {
/* assuming data was returned by malloc, thus is properly aligned */
size_t i = 0, n = length / sizeof(size_t);
const size_t *pw = data;
const unsigned char *pb = data;
size_t val;
#define UNROLL_FACTOR 8
#if UNROLL_FACTOR == 8
size_t n1 = n - n % UNROLL_FACTOR;
for (; i < n1; i += UNROLL_FACTOR) {
val = pw[i + 0] | pw[i + 1] | pw[i + 2] | pw[i + 3] |
pw[i + 4] | pw[i + 5] | pw[i + 6] | pw[i + 7];
if (val)
return 0;
}
#endif
val = 0;
for (; i < n; i++) {
val |= pw[i];
}
for (i = n * sizeof(size_t); i < length; i++) {
val |= pb[i];
}
return val == 0;
}
Depending on your specific problem, it might be more efficient to detect non zero values early or late:
If the all zero case is the most common, you should compute cumulate all bits into the val accumulator and test only at the end.
If the all zero case is rare, you should check for non zero values more often.
The unrolled version above is a compromise that tests for non zero values every 64 or 128 bytes depending on the size of size_t.
Depending on your compiler and processor, you might get better performance by unrolling less or more. You could also use intrinsic functions available for your particular architecture to take advantage of vector types, but it would be less portable.
Note that the code does not verify proper alignment for the data pointer:
it cannot be done portably.
it assumes the data was allocated via malloc or similar, hence properly aligned for any type.
As always, benchmark different solutions to see if it makes a real difference. This function might not be a bottleneck at all, writing a complex function to optimize a rare case is counterproductive, it makes the code less readable, more likely to contain bugs and much less maintainable. For example, the assumption on data alignment may not hold if you change memory allocation scheme or if you use static arrays, the function may invoke undefined behavior then.
The following checks if the first byte is what you want, and all subsequent pairs of bytes are the same.
int check_bytes(const char * const data, size_t length, const char val)
{
if(length == 0) return 1;
if(*data != val) return 0;
return memcmp(data, data+1, length-1) ? 0 : 1;
}
int check_bytes64(const char * const data, size_t length, const char val)
{
const char * const aligned64_start = (char *)((((uintptr_t)data) + 63) / 64 * 64);
const char * const aligned64_end = (char *)((((uintptr_t)data) + length) / 64 * 64);
const size_t start_length = aligned64_start - data;
const size_t aligned64_length = aligned64_end - aligned64_start;
const size_t end_length = length - start_length - aligned64_length;
if (!check_bytes(data, start_length, val)) return 0;
if (!check_bytes(aligned64_end, end_length, val)) return 0;
return memcmp(aligned64_start, aligned64_start + 64, aligned64_length-64) ? 0 : 1;
}
A more elaborate version of this function should probably pass cache-line-aligned pointers to memcmp, and manually check the remaining blocks(s) instead of just the first byte.
Of course, you will have to profile on your specific hardware to see if there is any speed benefit of this method vs others.
If anyone doubts whether this works, ideone.
I once wrote the following function for my own use. It assumes that the data to check is a multiple of a constant chunk size and aligned properly for a buffer of machine words. If this is not given in your case, it is not hard to loop for the first and last few bytes individually and only check the bulk with the optimized function. (Strictly speaking, it is undefined behavior even if the array is properly aligned but the data has been written by any type that is incompatible with unsigned long. However, I believe that you can get pretty far with this careful breaking of the rules here.)
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
bool
is_all_zero_bulk(const void *const p, const size_t n)
{
typedef unsigned long word_type;
const size_t word_size = sizeof(word_type);
const size_t chunksize = 8;
assert(n % (chunksize * word_size) == 0);
assert((((uintptr_t) p) & 0x0f) == 0);
const word_type *const frst = (word_type *) p;
const word_type *const last = frst + n / word_size;
for (const word_type * iter = frst; iter != last; iter += chunksize)
{
word_type acc = 0;
// Trust the compiler to unroll this loop at its own discretion.
for (size_t j = 0; j < chunksize; ++j)
acc |= iter[j];
if (acc != 0)
return false;
}
return true;
}
The function itself is not very smart. The main ideas are:
Use large unsigned machine words for data comparison.
Enable loop unrolling by factoring out an inner loop with a constant iteration count.
Reduce the number of branches by ORing the words into an accumulator and only comparing it every few iterations against zero.
This should also make it easy for the compiler to generate vectorized code using SIMD instructions which you really want for code like this.
Additional non-standard tweaks would be to annotate the function with __attribute__ ((hot)) and use __builtin_expect(acc != 0, false). Of course, the most important thing is to turn on your compiler's optimizations.

how to prove the correctness of a c program with coq

I want to prove the correctness of some of my programs but I don't know where to start. Let's say I have the following program, how can I prove its correctness or lack there of. How can I go from the source below and plug them into a theorem prover. Coq or ACL2 or pretty much anything.
The code below just counts the bytes that it reads from the standard input. It has 2 versions, one does a byte by byte count, the other reads them by unsigned integer size chunks when possible. I know it's not portable or pretty, it's just an example that could get me started. With some help.
The code works and I know it's correct and I know how to write unit tests for it but I don't know how to prove anything about it.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
unsigned count_bytes1(unsigned char * bytes, unsigned len) {
unsigned count=0;
unsigned i;
for (i=0;i<len;i++) {
count+=bytes[i];
}
return count;
}
unsigned count_word(unsigned word) {
unsigned tmp = word;
if (sizeof(unsigned)==4) {
tmp = (0x00FF00FFU&tmp) + (( (0xFF00FF00U)&tmp)>>8);
tmp = (0x0000FFFFU&tmp) + (( (0xFFFF0000U)&tmp)>>16);
return tmp;
}
if (sizeof(unsigned)==8) {
tmp = (0x00FF00FF00FF00FFU&tmp) + (( (0xFF00FF00FF00FF00U)&tmp)>>8);
tmp = (0x0000FFFF0000FFFFU&tmp) + (( (0xFFFF0000FFFF0000U)&tmp)>>16);
tmp = (0x00000000FFFFFFFFU&tmp) + (( (0xFFFFFFFF00000000U)&tmp)>>32);
return tmp;
}
return tmp;
}
unsigned count_bytes2(unsigned char * bytes, unsigned len) {
unsigned count=0;
unsigned i;
for (i=0;i<len;) {
if ((unsigned long long)(bytes+i) % sizeof(unsigned) ==0) {
unsigned * words = (unsigned *) (bytes + i);
while (len-i >= sizeof(unsigned)) {
count += count_word (*words);
words++;
i+=sizeof(unsigned);
}
}
if (i<len) {
count+=bytes[i];
i++;
}
}
return count;
}
int main () {
unsigned char * bytes;
unsigned len=8192;
bytes=(unsigned char *)malloc(len);
len = read (0,bytes,len);
printf ("%u %u\n",count_bytes1(bytes,len),count_bytes2(bytes,len));
return 0;
}
1. Know what you are proving: specification
First, decide what it is you want to prove for your function. For instance, write a contract for your function, using the ACSL specification language:
/*# ensures \result >= x && \result >= y;
ensures \result == x || \result == y;
*/
int max (int x, int y);
2. Verification
Then, you may prove that your implementation satisfies the specification, for instance with Frama-C's WP plug-in.
The WP plug-in will generate proof obligations, the verification of which will ensure that the implementation is correct with respect to the specification. You can prove these in Coq 8.4+ if it amuses you (but almost nobody who actually does this does not first apply available, fully automatic SMT provers such as Alt-Ergo).
PS: it appears that you are trying to prove that one C function is equivalent to another, that is, to use a simple C function as specification for an optimized one. Proving the equivalence of one with respect to the other is the approach followed in this article:
José Bacelar Almeida, Manuel Barbosa, Jorge Sousa Pinto, and Bárbara Vieira.
Verifying cryptographic software correctness with respect to reference implementations. In FMICS’09, volume 5825 of LNCS, pages 37–52, 2009.

Boolean array using "char"

I made an object that actually represents an array of 8 booleans stored in a char. I made it to learn something more about bitwise operators and about creating your own objects in C. So I've got two questions:
Can I be certain if the below code
always works?
Is this a good implementation to
make an object that can't get lost
in C, unless you release it
yourself.
The Code:
/*
* IEFBooleanArray.h
* IEFBooleanArray
*
* Created by ief2 on 8/08/10.
* Copyright 2010 ief2. All rights reserved.
*
*/
#ifndef IEFBOOLEANARRAY_H
#define IEFBOOLEANARRAY_H
#include <stdlib.h>
#include <string.h>
#include <math.h>
typedef char * IEFBooleanArrayRef;
void IEFBooleanArrayCreate(IEFBooleanArrayRef *ref);
void IEFBooleanArrayRelease(IEFBooleanArrayRef ref);
int IEFBooleanArraySetBitAtIndex(IEFBooleanArrayRef ref,
unsigned index,
int flag);
int IEFBooleanArrayGetBitAtIndex(IEFBooleanArrayRef ref,
unsigned index);
#endif
/*
* IEFBooleanArray.c
* IEFBooleanArray
*
* Created by ief2 on 8/08/10.
* Copyright 2010 ief2. All rights reserved.
*
*/
#include "IEFBooleanArray.h"
void IEFBooleanArrayCreate(IEFBooleanArrayRef *ref) {
IEFBooleanArrayRef newReference;
newReference = malloc(sizeof(char));
memset(newReference, 0, sizeof(char));
*ref = newReference;
}
void IEFBooleanArrayRelease(IEFBooleanArrayRef ref) {
free(ref);
}
int IEFBooleanArraySetBitAtIndex(IEFBooleanArrayRef ref, unsigned index, int flag) {
int orignalStatus;
if(index < 0 || index > 7)
return -1;
if(flag == 0)
flag = 0;
else
flag = 1;
orignalStatus = IEFBooleanArrayGetBitAtIndex(ref, index);
if(orignalStatus == 0 && flag == 1)
*ref = *ref + (int)pow(2, index);
else if(orignalStatus == 1 && flag == 0)
*ref = *ref - (int)pow(2, index);
return 0;
}
int IEFBooleanArrayGetBitAtIndex(IEFBooleanArrayRef ref, unsigned index) {
int result;
int value;
value = (int)pow(2, index);
result = value & *ref;
if(result == 0)
return 0;
else
return 1;
}
I'm more of an Objective-C guy, but I really want to learn C more. Can anyone request some more "homework" which I can improve myself with?
Thank you,
ief2
Don't check unsigned types with < 0, it's meaningless and causes warnings on some compilers.
Don't use unsigned types without specifying their size (unsigned int, unsigned char, etc).
If flag == 0 why are you setting it to 0?
I don't like abstracting the * away in a typedef, but it's not wrong by any means.
You don't need to call memset() to set a single byte to 0.
Using pow to calculate a bit offset is crazy. Check out the << and >> operators and use those instead
Fully parenthesize your if statement conditions or be prepared for debugging pain in your future.
If you use the bitwise operators & and | instead of arithmetic + and - in your SetBitAtIndex function, you won't need all those complicated if statements anyway.
Your GetBitAtIndex routine doesn't bounds check index.
From that list, #9 is the only one that means your program won't work in all cases, I think. I didn't exhaustively test it - that's just a first glance check.
pow(2,index) is among the more inefficient ways to produce a bit mask. I can imagine that using the Ackermann function could be worse, but pow() is pretty much on the slow side. You should use (1<<index) instead. Also, the C'ish way to set/clear a bit in a value looks different. Here's a recent question about this:
Simple way to set/unset an individual bit
If you want to munge bits in C in an efficient and portable way, then you really should have a look at the bit twiddling page, that everyone here will suggest to you if you mention "bits" somehow:
http://graphics.stanford.edu/~seander/bithacks.html
The following code sequence:
if(result == 0)
return 0;
else
return 1;
can be written as return (result != 0);, return resultor return !!result (if result should be forced to 0 or 1) . Though it's always a good idea to make an intent clear, most C programmer will prefer 'result result;' because in C this the way to make your intent clear. The if looks iffy, like a warning sticker saying "Original developer is a Java guy and knows not much about bits" or something.
newReference = malloc(sizeof(char));
memset(newReference, 0, sizeof(char));
malloc + memset(x,0,z) == calloc();
You have a way to report an error (invalid index) for IEFBooleanArraySetBitAtIndex but not for IEFBooleanArrayGetBitAtIndex. This is inconsistent. Make error reporting uniform, or the users of your library will botch error checking.
As for accessing bit #n in your char object, instead of using pow() function, you can use shifting and masking:
Set bit #n:
a = a | (1 << n);
Clear bit #n:
a = a & (~(1 << n));
Get bit #n:
return ((a >> n) & 1);
Nobody seems to be mentioning this (I am surprised), but... You can't tell me you're seriously doing malloc(sizeof(char))? That is a very small allocation. It doesn't make sense to make this a heap allocated object. Just declare it as char.
If you want to have some degree of encapsulation, you can do: typedef char IEFBoolArray; and make accessor functions to manipulate an IEFBoolArray. Or even do typedef struct { char value; } IEFBoolArray; But given the size of the data it would be sheer madness to allocate these one at a time on the heap. Have consumers of the type just declare it inline and use the accessors.
Further... Are you sure you want it to be char? You might get slightly better code generated if you promote that to something larger, like int.
In addition to Carl Norum points:
Don't save space in char such way unless you have to (i.e. you store a lot of bit values). It is much slower as you have to perform bitwise operations etc.
On most architectures you waste memory by mallocing char. One pointer takes 4 to 8 times more then char on most modern architectures and additionally you have data about the malloced chunk as it.
Probably static size is not the best approach as it inflexible. I wouldn't see any benefit of using speciall functions for it.
As of 3rd point something like:
typedef struct {
uint64_t size;
uint64_t *array;
}bitarray;
bitarray bitarray_new(uint64_t size) {
bitarray arr;
arr.size = size;
arr.array = calloc(size/8);
return arr;
}
void bitarray_free(bitarray arr) {
free(arr.array);
}
void bitarray_set(bitarray arr, uint64_t index, int bit) {
assert (index <= arr.size)
if (bit)
array[index/8] |= 1 << (index % 8);
else
array[index/8] ^= ~(1 << (index % 8));
}
void bitarray_get(bitarray arr, uint64_t index, int bit) {
assert (index <= arr.size)
return array[index/8] & 1 << (index % 8);
}
Copyright 2010 ief2. All rights reserved.
Actually they are not. You volontarly published them under cc-by-sa licence and only some right are reserved. Additionally you want us to read and modify the code so reserving all right is pointless.
(PS. I would advice against publishing trivial work under restrictive licences anyway - it does not look professionaly - unless you have legal issues to do so)
Is this a good implementation to make an object that can't get lost in C, unless you release it yourself.
Sorry?

Resources