[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Programming question: sizeof struct?



Hi,

> Hi there,
> 
> The upstream maintainer of one of my packages is having problems
> with his code. 

with his code?

> I thought it would be nice to use the debian mailing
> lists, to see if we can an answer on this. I will forward any solution
> to him.
> 
> ----------------------------------------------------------------------
> The reason why I have not released LogConv 1.54 for Linux is that I am
> having problems with packed structures that is causing some file formats
> to not be handled properly.  Even though I specify -fpack_struct the
> generated code does not appear to actually do this.  Structure fields
> are
> offset and the return from sizeof() returns a value that is not valid.

what do you mean it is not valid? Read ANSI standard or any
decent C book. It is quite valid - compiler may insert any padding
between struct members...

> For instance, if the structure were:
> 
> struct foo {
>     char text[3];
>     int  num;
> };
> 
> sizeof would return 6 and not 5.  

6? Are you sure you're using Linux/gcc?

The sizeof should be equal to 8 ! It would be 6 only on 16bit platform
(MS DOS comes to mind...)

> So it's obvious that the compiler is
> placing a pad byte between text and num to align num.  I want it to
> stop!
> -----------------------------------------------------------------------
> 
> Thanks for your attention.
> 
> Joop

from GCC info manual:

Specifying Attributes of Variables
==================================

   The keyword `__attribute__' allows you to specify special attributes
of variables or structure fields.  This keyword is followed by an
attribute specification inside double parentheses.  Eight attributes
are currently defined for variables: `aligned', `mode', `nocommon',
`packed', `section', `transparent_union', `unused', and `weak'.  Other
attributes are available for functions (*note Function Attributes::.)
and for types (*note Type Attributes::.).

   You may also specify attributes with `__' preceding and following
each keyword.  This allows you to use them in header files without
being concerned about a possible macro of the same name.  For example,
you may use `__aligned__' instead of `aligned'.

...

`packed'
     The `packed' attribute specifies that a variable or structure field
     should have the smallest possible alignment--one byte for a
     variable, and one bit for a field, unless you specify a larger
     value with the `aligned' attribute.

     Here is a structure in which the field `x' is packed, so that it
     immediately follows `a':

          struct foo
          {
            char a;
            int x[2] __attribute__ ((packed));
          };

But i advise to use gcc default padding - it is definitely
more efficient and portable...

IMO, you have problems with structs I/O. The UNIX paradigm is that
files have no structure, they're just byte streams... And in order to
be endian-neutral, choose some byte ordering - for example, lets keep
network byte ordering in the file

Provide two small functions like

write_struct_foo( int fd, const struct foo* ptr ) {
  
  int tmp = htonl( ptr->num );
  write( fd, ptr->name, 3 );
  write( fd, &tmp, sizeof(int) );

}

read_struct_foo( int fd, struct foo* ptr ) {
  int tmp;

  read( fd, ptr->name, 3 );
  read( fd, &tmp, sizeof(int) );

  ptr->num = ntohl( tmp );

}

Of course, error checking and other stuff should be added but hope 
you got the idea. Just in case, some info from glibc doc:

Byte Order Conversion
---------------------

   Different kinds of computers use different conventions for the
ordering of bytes within a word.  Some computers put the most
significant byte within a word first (this is called "big-endian"
order), and others put it last ("little-endian" order).

   So that machines with different byte order conventions can
communicate, the Internet protocols specify a canonical byte order
convention for data transmitted over the network.  This is known as the
"network byte order".

   When establishing an Internet socket connection, you must make sure
that the data in the `sin_port' and `sin_addr' members of the
`sockaddr_in' structure are represented in the network byte order.  If
you are encoding integer data in the messages sent through the socket,
you should convert this to network byte order too.  If you don't do
this, your program may fail when running on or talking to other kinds
of machines.

   If you use `getservbyname' and `gethostbyname' or `inet_addr' to get
the port number and host address, the values are already in the network
byte order, and you can copy them directly into the `sockaddr_in'
structure.

   Otherwise, you have to convert the values explicitly.  Use `htons'
and `ntohs' to convert values for the `sin_port' member.  Use `htonl'
and `ntohl' to convert IPv4 addresses for the `sin_addr' member.
(Remember, `struct in_addr' is equivalent to `uint32_t'.)  These
functions are declared in `netinet/in.h'.

 - Function: uint16_t htons (uint16_t HOSTSHORT)
     This function converts the `uint16_t' integer HOSTSHORT from host
     byte order to network byte order.

 - Function: uint16_t ntohs (uint16_t NETSHORT)
     This function converts the `uint16_t' integer NETSHORT from
     network byte order to host byte order.

 - Function: uint32_t htonl (uint32_t HOSTLONG)
     This function converts the `uint32_t' integer HOSTLONG from host
     byte order to network byte order.

     This is used for IPv4 internet addresses.

 - Function: uint32_t ntohl (uint32_t NETLONG)
     This function converts the `uint32_t' integer NETLONG from network
     byte order to host byte order.

     This is used for IPv4 internet addresses.



regards

OK


Reply to: