HELP: Understanding basic C and bitfields...

Hey,

OK so I do not get a few things and must resort to begging assistance. :stuck_out_tongue:

First off, what exactly is the point of caring about a variable's address? Shouldn't the environment take care of that, especially something that's intended to be an IDE? I don't see what purpose referencing &variable is for, could someone please explain?

Secondly, how do you manipulate packed bit fields? Let me give an example and see if I have any clue:

struct hours_byte
{
 unsigned int format: 1;     // range: 0-1, 0=24h 1=12h
 unsigned int ampm: 1;       // range: 0-1, 0=AM 1=PM
 unsigned int tenshours: 2;  // range: 0-2 
 unsigned int hours: 4;      // range: 0-15 (guessing it never goes over 9)
};

So in this case, interfacing to an RTC, hours are broken up and the tens of hours are a separate field? so 2am is 00000010, but 11pm is 00100011? I understand that this, when translated from BIN to HEX, will be 2 and 23, respectively. But it seems stupid to me so comprehension does not come easily. Why? And if the hours field is 4 bits, that means it could possibly go over 9, do I have to write a check routine since in this layout, 11am could both be 00010001 or 00001011 ?

I guess the main point is this: How do I manipulate individual bits in a bitfield, both reading and writing?

Thanks for reading.

First off, what exactly is the point of caring about a variable's address? Shouldn't the environment take care of that, especially something that's intended to be an IDE? I don't see what purpose referencing &variable is for, could someone please explain?

This is because if you want to send something of a large scale. Say an object that is 200 bytes, if you do not pass the adress, the processor will need to copy all those bytes, instead of just the adress.

Actually pretty clever.

Pointers

Secondly, how do you manipulate packed bit fields? Let me give an example and see if I have any clue:

struct hours_byte

{
unsigned int format: 1;     // range: 0-1, 0=24h 1=12h
unsigned int ampm: 1;       // range: 0-1, 0=AM 1=PM
unsigned int tenshours: 2;  // range: 0-2
unsigned int hours: 4;      // range: 0-15 (guessing it never goes over 9)
};




I guess the main point is this: How do I manipulate individual bits in a bitfield, both reading and writing?

You should adress that struct as all other structs:

myStructInstance.myField = var;
var = myStructInstance.myField;

As for manipulating bits see: http://arduino.cc/en/Reference/BitSet and http://arduino.cc/en/Reference/BitRead
:slight_smile:

Bits are easy:-
To set a bit OR it with a 1 (using the bitwise OR operation | )
To clear a bit AND it with 0 (using the bitwise AND operation & )
To togle a bit (change it's state) Exclusive OR it with a 1 (using the bitwise EOR operation ^ )

To leave a bit unchanged during an OR operation use a 0
To leave a bit unchanged during an EOR operation use a 0
To leave a bit unchanged during an AND operation use a 1

You have to do all the bits in a byte at the same time hence you want to change some and leave others alone.

Bits are easy:-
To set a bit OR it with a 1 (using the bitwise OR operation | )
To clear a bit AND it with 0 (using the bitwise AND operation & )
To togle a bit (change it's state) Exclusive OR it with a 1 (using the bitwise EOR operation ^ )

To leave a bit unchanged during an OR operation use a 0
To leave a bit unchanged during an EOR operation use a 0
To leave a bit unchanged during an AND operation use a 1

Thank you so much! That was wisdom.
I've got to remember that!

I'm now inspired enough to indulge port manipulation.

You have to do all the bits in a byte at the same time hence you want to change some and leave others alone.

If you do the declaration as was shown above (copied here), you don't have to deal with the bits and bytes; the compiler adds that code for you. You just treat each field as an independent integer that has less range than a normal integer.

struct hours_byte
{
 unsigned int format: 1;     // range: 0-1, 0=24h 1=12h
 unsigned int ampm: 1;       // range: 0-1, 0=AM 1=PM
 unsigned int tenshours: 2;  // range: 0-2
 unsigned int hours: 4;      // range: 0-15 (guessing it never goes over 9)
};

To use it,

hours_byte now;

// four in the afternoon, in 24hr format.
now.tenshours = 1;
now.hours = 6;
now.format = 0;
now.pm = 0;
dumpHour(now);

// four in the afternoon, in 12hr format.
now.tenshours = 0;
now.hours = 4;
now.format = 1;
now.pm = 1;
dumpHour(now);

// five in the morning, in 12hr format.
now.tenshours = 0;
now.hours = 5;
now.format = 1;
now.pm = 0;
dumpHour(now);

And to read it out in human and binary forms:

void dumpHour(hours_byte when)
{
    Serial.print(when.tenshours, DEC);
    Serial.print(when.hours, DEC);
    Serial.print(":00");
    if (when.format == 1)
        Serial.print((char*)(when.pm? "pm" : "am"));
    Serial.print(" == ");
    Serial.println(*(byte*)(hours_byte*)&when, BIN);
}

I'm too lazy to make the full hours_byte suite of code right now, but you can say now.hours++ or now.tenshours = now.hours % 10 or other expressions, and the C compiler will add all the implied bit masking off and bit shifting around necessary to do the math.

C bitfields are pretty slick, but the concept is a bit foreign to high-level coders. I'm surprised we don't see more of this in Arduino land.

Wow, thanks for the replies. and so fast! I'm guessing you guys been on the questioning side a few times.

I still don't get the whole & and * thing, but since both questions lead to conceptual answers, I'll delve back into my C books and try to grasp it.

If anyone's got a clever metaphoric or analogous answer about why variable address location referencing and dereferencing is so important, and can explain it better than Kernighan & Ritchie, I would love to hear it. Probably deserves to be Playground'ed though, as what AlphaBeta linked me to above didn't help clarity in the least. :-?

As well as bitfields, everyone has more knowledge on the subject than me, but man - halley, if that's you being "too lazy" I'm glad, you motivated and on coffee would talk so far over me as to be wasted effort - in this case, it was perfect laziness. Exactly what I was grasping for!

Thanks everyone!


PS: How the frack do you Quote: on YaBB? 'cause

isn't working, and I see no reply-with-quote button? (might want to PM that as it's not thread relevant.)

Wow, thanks for the replies. and so fast! I'm guessing you guys been on the questioning side a few times.

I often still am :slight_smile:

If anyone's got a clever metaphoric or analogous answer about why variable address location referencing and dereferencing is so important, and can explain it better than Kernighan & Ritchie, I would love to hear it.

I tried to make one and come up with this:

If you want a friend to buy something for you in new york, would you rather build him a replica of NY or just give him an adress?

And ofcourse you intuitively think, 'give the adress' because you know how much time and effort you will save doing so. Its basically the same with pointers [reference dereference]

PS: How the frack do you Quote: on YaBB? 'cause

isn't working, and I see no reply-with-quote button? (might want to PM that as it's not thread relevant.)

I add 'quote=POSTNUMBER' to the url.

This is the url I used to get to quote you:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?action=post;num=1237406231;quote=5;title=PostReply

In C, machine addresses can be manipulated as easily as any other data type, such as integers and characters. So, we need a way to write code that is equivalent to writing an integer constant, but for addresses. If we want to assigna a value to an integer, we can write:

int i;
i = 42;

We can do the same with pointers, but we can't just write an arbitrary number and expect it to be a valid address. Instead, we need to find the address of some exisiting piece of data (e.g. the integer 'i'):

int *p;
p = &i;

We use '*' meaning 'pointer to', and '&' meaning 'address of', i.e. 'p' is a pointer to an integer, and it is given the value that is the address of 'i'. We abbreviate that and say 'p points to i'.

We use '*' meaning 'pointer to'

Actually I would suggest the use of 'points to' (or point or something similar) because:

int *p;

p = &i;

Can be extended to one more operation, that often seems strange to people that thinks of * being a 'pointer' initializerthingy.

*p = 3;

This becomes:
'set what p points to, to 3' or
point p [the adress of i] to 3

even simpler:
i = 3;

So in effect, *p or i is exactly the same.

I really don't want to drive this into the ground, being my first thread and all..

I guess what I don't understand, is why this is necessary... in this day and age, shouldn't the interpreter / compiler take care of such minutiae? I mean OK so it's faster to reference the location vs. having to load & copy the variable value, but couldn't all variable referencing be converted to this access method by the compiler as it generates executable code?

Is C the only language you do this in?

Thanks for trying to help me grasp this, AB. But forcing it isn't working. I guess I'll soak it up through osmosis, eventually. :-?

(this from a guy that thinks "structured logic" is an oxymoron.)

...but couldn't all variable referencing be converted to this access method by the compiler as it generates executable code?...

There are some times you'll want to use a value without changing the original variable. :slight_smile:

There was a language called "Bliss" that used pointers to things as the default sort of access, even for "simple" data types like characters and integers. It was a really pain in the neck. The nice thing about a pointer is that it can point to pretty much anything. While it's true that you can make do with pointer for all variable access, it just isn't NATURAL to do so when the data in question is simple enough. You want to say "A = B+C", not "set the contents of A to the sum of the contents of B and the contents of C." It's more ambiguous for a structure, and in fact "large" C programs DO tend to use pointers to structures almost exclusively. Most computer languages have features that aren't frequently used.

Another advantage of non-pointer data structures is when you end up embedding one data structure inside another. If the smaller structure is embedded as a pointer, then you actually have two chunks of memory that may need "management" instead of one.

Nearly all serious computer languages have some sort of pointer capability.
Whether arguments passed to subroutines should be passed as pointers to the original or copies is widely argued without a clear winner. C is widely cursed for its inconsistency in that area (particularly for arrays (passed by pointers) vs structures (passed by copying.)) Some fortran compilers passed all arguments as pointers, and were famous for their ability to change the values of constants when the subroutine mistakenly modified a value that had been a constant on the way in...

I guess what I don't understand, is why this is necessary... in this day and age, shouldn't the interpreter / compiler take care of such minutiae?

Well... yeah, many higher level languages abstract all that stuff out of the way. The thing to remember about C is that it's not really a language from this day and age. It hearkens back to the early days, when all computers were room-filling mainframes, and RAM was measured in kilobytes. It is often joked that C a language with all the power of assembly language, and all the readability of assembly language. In other words, it as only barely a single step away from the raw machine code the processor uses. As a result, C has available all the funky "a variable is just a number, stored at an address" syntax that you find in raw assembly language. Given the simplicity and limited resources of microcontrollers, the popularity of C for programming them is unsurprising.

it as only barely a single step away from the raw machine code the processor uses.

I don't know how anyone looking at any of the C code in all the example sketches could think that. :wink:

It is really worth gettting your head around pointers as life is so much easier when you understand them and your code will be more efficient as it has been pointed out with a large variable example above.

To get your head around pointers, the best advice was in a book I read years ago:

An asterisk () looks like a star
whenever you see a star (
), think of it as the word store
so when you see:
*p = somevalue;
somevalue is stored (stared) in the address pointed to by p

stuff like this is so easy with pointers. Lets say you want to copy some data to a new string and deltee the new line '\n' character.

char source[21], dest[20];
char *s, *d;
strcpy(source,"line 1\nline*2);
s = source;
p = dest;

It is really worth gettting your head around pointers as life is so much easier when you understand them and your code will be more efficient as it has been pointed out with a large variable example above.

To get your head around pointers, the best advice was in a book I read years ago:

An asterisk () looks like a star
whenever you see a star (
), think of it as the word store
so when you see:
*p = somevalue;
somevalue is stored (stared) in the address pointed to by p

Stuff like this is so easy with pointers and the code is very efficient. Lets say you want to copy a string into a buffer and delete the new line character '\n' that you just read in from a serial port. Hopefully this is the code you need.

char source[21], destination[21];
char *s, *d;

strcpy(source,"Line1\nLine2\n");
s = source;
d = destination;

while(*s){
    if(*s == '\n')
        s++;  //Skip New line character
    *d++ = *s++;
}
*d = '\0';   // Terminate the destination string

The other advantage of pointers is that it lets one function alter a variable in another. so you might like to make this a function in its own right eg

void cullnewline( char *d,char *s)
{
 while(*s){
    if(*s == '\n')
        s++;  //Skip New line character
    *d++ = *s++;
}
*d = '\0';   // Terminate the destination string
}

The ease that which C gets down to low levels is its strength. Use it to advantage. It is actually your friend.

It is often joked that C a language with all the power of assembly language, and all the readability of assembly language.

It's funny, I've heard the complaint before that C is hard to read, but I've always found programs in C to be fairly easy to grasp the function of with a bit of thought, and I've never been a talented coder. Unlike some code from the-language-which-shall-not-be-named:

print "Enter a user name: "; $name = ; system("stty -echo"); print "Password: "; $pass = ; system("stty echo"); print "\n"; chop($name); chop($pass); print &CryptPasswd($name,$pass); print "\n"; sub CryptPasswd { local($ph_alias,$clearpasswd) = @_; @saltset = ('a' .. 'z','A' .. 'Z', '0' .. '9','.','/'); $now = time(); ($pert1, $pert2) = unpack ("C2",$ph_alias); $week = $now / (606024*7) + $pert1 + $pert2; $nsalt = $saltset[$week % 64] . $saltset[$now % 64]; $cryptpass = crypt($clearpasswd,$nsalt); return($cryptpass);} :o