assigning bit-field structure

  • Thread starter - Kees van der Bent -
  • Start date
K

- Kees van der Bent -

/* With the following: */
typedef struct
{
unsigned char a : 1;
unsigned char b : 1;
} sss_t;
sss_t sss;
unsigned char ppp;

main()
{
sss = (sss_t)ppp; /* why 'invalid cast expression'? */

sss = *(sss_t*)&ppp; /* why OK? */
}

/*
Used Sun compiler.

Thanks,

Kees
*/
 
R

Rakesh

- Kees van der Bent - said:
/* With the following: */
typedef struct
{
unsigned char a : 1;
unsigned char b : 1;
} sss_t;
sss_t sss;
unsigned char ppp;

main()
{
sss = (sss_t)ppp; /* why 'invalid cast expression'? */

You are trying to convert an unsigned char to an user-defined data
type, that are noway related , as far as the compiler is concerned.
sss = *(sss_t*)&ppp; /* why OK? */

&ppp changes it to UCHAR * , and then you are converting to (sss_t
*) [ it really gets dangerous here, but you are allowed to do it, since
they are addresses after all ] .
Having converted it, you are dereferencing it back. I am assuming you
are writing this for learning purposes only. But as such , such cast
expressions ( changing pointers of a given type to a diff. type ) ought
not to be used in any serious project ;) .

HTH .
 
P

Peter Pichler

- Kees van der Bent - said:
/* With the following: */
typedef struct
{
unsigned char a : 1;
unsigned char b : 1;
} sss_t;
sss_t sss;
unsigned char ppp;

Why are these global?
main()
{
sss = (sss_t)ppp; /* why 'invalid cast expression'? */

You're not gonna like it, but you can't do it, because... you can't do it!
You cannot cast between a scalar and an aggregate type. Think of it this
way: an aggregate (such as a structure) is an object containing more than
one value (even if the structure contains only one field, we are talking in
general terms here). A scalar only contains one value. How would you convert
from one to another? What sense would it make?
sss = *(sss_t*)&ppp; /* why OK? */

Conversions between pointers are allowed. Whether the result of such
conversion is meaningful, or even a valid pointer, is quite another
question.
}

/*
Used Sun compiler.

Doesn't matter.
Thanks,

Kees
*/

Peter
 
C

Christopher Benson-Manica

Rakesh said:
&ppp changes it to UCHAR * , and then you are converting to (sss_t
^^^^^
ITYM "unsigned char". UCHAR is not a C keyword, although of course
you can typedef it yourself if you're so inclined.
 
M

Martin Ambuhl

- Kees van der Bent - said:
/* With the following: */
typedef struct
{
unsigned char a : 1;
unsigned char b : 1;

unsigned char is not a valid type for a bit field.
} sss_t;
sss_t sss;
unsigned char ppp;

main()
{
sss = (sss_t)ppp; /* why 'invalid cast expression'? */

ppp is a scalar; the type sss_t isn't.
sss = *(sss_t*)&ppp; /* why OK? */

Because C lets you do all sorts of silly things using pointers.
 
R

Rakesh

Oh yeah :) of course , i meant unsigned char . I was a bit too lazy to
type in that. Thanks for pointing it out, though.
 
K

- Kees van der Bent -

Martin Ambuhl said:
unsigned char is not a valid type for a bit field.

Yes I read this in K&R and wondered why the compiler did not
warn at all. What do you think?
 
K

- Kees van der Bent -

Peter Pichler said:
Why are these global?

I quickly typed in a small piece of code that gave the
same results as my real code. I don't like globals either.
You're not gonna like it, but you can't do it, because... you can't do it!
You cannot cast between a scalar and an aggregate type. Think of it this
way: an aggregate (such as a structure) is an object containing more than
one value (even if the structure contains only one field, we are talking in
general terms here). A scalar only contains one value. How would you convert
from one to another? What sense would it make?

I like to view a bit-field structure as a special case of of scalar:
a regular scalar with the added convenience of being able to 'address'
certain bits. Isn't this how bit-fields are used in hardware device
drivers (mapping certain registers)?

In my case my input data is of type unsigned char which needs to be
mapped/assigned on/to a bit-field structure. Instead of writing out
all the nuts al bolts (with ... >> 4 & 0x3 ...) I want C to help me
out here and let the compiler do the work for me.
Conversions between pointers are allowed. Whether the result of such
conversion is meaningful, or even a valid pointer, is quite another
question.

I agree with you. But this seems to be the only way to get the result
I want. Although I'm confident that this will work in practice, the
question remains if this works according to the C standard. How is bit-
field structure allignment specified in the C spec?
 
M

Martin Ambuhl

- Kees van der Bent - said:
Yes I read this in K&R and wondered why the compiler did not
warn at all. What do you think?

That you either don't have the diagnostic level set properly or that you
are not invoking your compiler in conforming mode.
 
C

Chris Torek

I like to view a bit-field structure as a special case of of scalar:
a regular scalar with the added convenience of being able to 'address'
certain bits. Isn't this how bit-fields are used in hardware device
drivers (mapping certain registers)?

Some people write code like that. Others write code that survives
changing compilers. :)
In my case my input data is of type unsigned char which needs to be
mapped/assigned on/to a bit-field structure. Instead of writing out
all the nuts al bolts (with ... >> 4 & 0x3 ...) I want C to help me
out here and let the compiler do the work for me.

Unfortunately, as Dennis Ritchie himself once put it, C's bitfields
are "a botch and a blemish". For instance:
How is bit-field structure allignment specified in the C spec?

It is not.

Much worse, however, is the fact that two different compilers FOR THE
SAME MACHINE may reverse the order of the bits in some bitfield:

/* device connected to motorola 68010; C compiler has 16-bit int */
struct hardware_reg {
unsigned int go:1; /* set to 1 to make device run */
unsigned int cmd:3; /* command */
unsigned int ctl:4; /* control bits */
unsigned int dma:1; /* set to 1 to use DMA */
unsigned int :3; /* unused */
unsigned int status:4; /* device status */
unsigned int addr; /* DMA address */
};

Compiled with Compiler A, the "go" bit is (as desired) the high bit
of the first 16-bit word, but compiled with Compiler B, the "go" bit
becomes the *low* bit of that word.

Using "unsigned short csr, addr;" and defining bits for the control
and status register works on every compiler you will find for that
68010-based system (even those with 32-bit ints). C gives you (the
programmer) no control whatsoever of where the bits in the bitfield
wind up. If you want to write this sort of code, you might consider
Ada, which has the necessary construct (the "representation clause").
 
D

Dan Pop

In said:
How is bit-field structure allignment specified in the C spec?

It is not.

Much worse, however, is the fact that two different compilers FOR THE
SAME MACHINE may reverse the order of the bits in some bitfield:

/* device connected to motorola 68010; C compiler has 16-bit int */
struct hardware_reg {
unsigned int go:1; /* set to 1 to make device run */
unsigned int cmd:3; /* command */
unsigned int ctl:4; /* control bits */
unsigned int dma:1; /* set to 1 to use DMA */
unsigned int :3; /* unused */
unsigned int status:4; /* device status */
unsigned int addr; /* DMA address */
};

Compiled with Compiler A, the "go" bit is (as desired) the high bit
of the first 16-bit word, but compiled with Compiler B, the "go" bit
becomes the *low* bit of that word.

Using "unsigned short csr, addr;" and defining bits for the control
and status register works on every compiler you will find for that
68010-based system (even those with 32-bit ints). C gives you (the
programmer) no control whatsoever of where the bits in the bitfield
wind up.[/QUOTE]

It's a tradeoff between portability and readability. You can provide
two different definitions for struct hardware_reg and select between
them according to the compiler being used. Such code is using plenty of
extensions anyway, so it's not portable code.

Dan
 
K

- Kees van der Bent -

Dan said:
It is not.

Much worse, however, is the fact that two different compilers FOR THE
SAME MACHINE may reverse the order of the bits in some bitfield:

/* device connected to motorola 68010; C compiler has 16-bit int */
struct hardware_reg {
unsigned int go:1; /* set to 1 to make device run */
unsigned int cmd:3; /* command */
unsigned int ctl:4; /* control bits */
unsigned int dma:1; /* set to 1 to use DMA */
unsigned int :3; /* unused */
unsigned int status:4; /* device status */
unsigned int addr; /* DMA address */
};

Compiled with Compiler A, the "go" bit is (as desired) the high bit
of the first 16-bit word, but compiled with Compiler B, the "go" bit
becomes the *low* bit of that word.

Using "unsigned short csr, addr;" and defining bits for the control
and status register works on every compiler you will find for that
68010-based system (even those with 32-bit ints). C gives you (the
programmer) no control whatsoever of where the bits in the bitfield
wind up.


It's a tradeoff between portability and readability. You can provide
two different definitions for struct hardware_reg and select between
them according to the compiler being used. Such code is using plenty of
extensions anyway, so it's not portable code.

Dan[/QUOTE]

This sub-thread made me decide to make my code portable using >> & ...
Thanks for the help and sharing info about C and its implementations.

Kees
 
H

Henri Manson

- Kees van der Bent - said:
/* With the following: */
typedef struct
{
unsigned char a : 1;
unsigned char b : 1;
} sss_t;
sss_t sss;
unsigned char ppp;

main()
{
sss = (sss_t)ppp; /* why 'invalid cast expression'? */

sss = *(sss_t*)&ppp; /* why OK? */
}

/*
Used Sun compiler.

Thanks,

Kees
*/

There is an elegant way to solve your problem: use unions.
Here the c member represents the 'whole' unsigned char while a and b are
bits of that unsigned char.

#include <stdio.h>

typedef union
{
struct
{
unsigned char a : 1;
unsigned char b : 1;
} u;
unsigned char c;
} sss_t;

main()
{
sss_t sss;
sss.c = 1;
printf("sizeof(sss) = %d\n", sizeof(sss));
printf("sss.c = %d, sss.a = %d, sss.b = %d\n", sss.c, sss.u.a,
sss.u.b);
sss.c = 2;
printf("sss.c = %d, sss.a = %d, sss.b = %d\n", sss.c, sss.u.a,
sss.u.b);
return 0;
}

The output of the program is:

sizeof(sss) = 1
sss.c = 1, sss.a = 1, sss.b = 0
sss.c = 2, sss.a = 0, sss.b = 1

I used gcc-3.3.3. Please note that not all compilers will generate a
structure with size!
 
K

- Kees van der Bent -

Still the C spec does not specify the bit order of the fields. This
construct can therefore not be used for mapping hardware registers.
Thanks for the idea anyway; although I chose another implementation,
I did not think of using a union at all.

Cheers,

Kees
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top