FAQ 2.6

  • Thread starter Christopher Benson-Manica
  • Start date
K

Kevin D. Quitt

You don't even have to miss ONE place, it's underlined behavior
regardless. And almost always unnecessary trickery. I'd be willing
to bet that in 99% if the places where its used in place of a pointer
member pointing to a separately malloc'ed buffer it doesn't have a
noticeable performance impact on the program anyway.

Try building and working with network packets. Besides struct hack is
supported in C99, 6.7.2.1:

16 As a special case, the last element of a structure with more than one
named member may have an incomplete array type; this is called a flexible
array member. With two exceptions, the flexible array member is ignored.
First, the size of the structure shall be equal to the offset of the last
element of an otherwise identical structure that replaces the flexible
array member with an array of unspecified length.106) Second, when a . (or
->) operator has a left operand that is (a pointer to) a structure with a
flexible array member and the right operand names that member, it behaves
as if that member were replaced with the longest array (with the same
element type) that would not make the structure larger than the object
being accessed; the offset of the array shall remain that of the flexible
array member, even if this would differ from that of the replacement
array. If this array would have no elements, it behaves as if it had one
element but the behavior is undefined if any attempt is made to access
that element or to generate a pointer one past it.

17 EXAMPLE Assuming that all array members are aligned the same, after the
declarations:

struct s { int n; double d[]; };
struct ss { int n; double d[1]; };

the three expressions:

sizeof (struct s)
offsetof(struct s, d)
offsetof(struct ss, d)

have the same value. The structure struct s has a flexible array member d.
 
E

Eric Sosman

The said:
Of course not; and yes, that's exactly why; respectively.
Consider the following hypothetical memory layouts:

A) 4 bytes of int, 1 byte of char

B) 4 bytes of int, 1 byte of char, 3 bytes of padding

C) 4 bytes of int, 3 bytes of padding, 1 byte of char


Now, whose method works?

A B C

Eric's method yes yes yes

Herb's method yes * yes


The * marks the case in which Herb's method allocates three
more bytes of memory than necessary. Note that Eric's
method handles this case just fine.

Wrong, because this would never occure! That is because a char[] will
ever start at an aligned address - and yiu describes a way to givbe
the array an unaligned address.

Are you claiming that Case B will never occur? Every
compiler I've ever seen in the past twenty-five years has
used the Case B algorithm (sometimes adjusted for different
`int' sizes). Of course, the Standard permits any of A,B,C
and other variations as well.
What is when you have another member in the struct?

Your project requires the struct to change to

struct name {
struct NAME *pNect;
unsigned short activity : 3;
unsigned short extInfo : 1;
unsigned short reserved : 3;
unsigned short io :1;
int namelen;
char namestr[1];
};

(Aside: bit-fields of `unsigned short' type are non-portable.)
Without changing all the places you tries to access in an obscure way
one of the members must be changed to get name and namestr accessed
right, whereas only 2 or 3 places needs access to the new mebers.

The fundamental assumption of the struct hack -- and it *is*
a hack, IMHO -- is that the "expandable" element is the last in
the struct. As long as that requirement is met, the offsetof()
formulation is correct as written and need not be changed at all
when the struct grows or shrinks.
What is when your your environment an aliment requires 8 bytes? Then
yoy'll have

D) 4 bytes of int, 4 bytes padding, 1 byte of char[], 7 bytes
padding

sizeof(int) = 4
sizeof(struct) = 16

And you can't never address the namestring at all using the pointer
arithmetic the compiler has buil in.

Case D as described is certainly within the realm of
possibility, but the conditions don't imply the conclusion.
Specifically:

- It is still possible to form a perfectly good pointer
to the name string.

- It is still possible to access that same string with
an expression like `ptr->namestr'.

- ... provided, of course, that the struct hack actually
operates as expected, that is, that the undefined
behavior it invokes is the desired and usual outcome.
If the implementation does bounds-checking on the
namestr[1] array, this whole business is doomed anyhow.
Hacks are still hacks.
Never make code obvious! Hold it simple. Let the compiler do any dirty
work under cover for you - it is designed to do so.

Um, er, that's exactly what the offsetof() is for ...
I know of offsetoff longer than you went from the 3. class of your
scool.

False, unless you are prescient. C and offsetof() did not
exist at the time to which you refer.
why not simply
ptr = malloc(sizeof(struct name) + strlen(name));
It is clear, secure code - and at least it is more maintenaceable.

It is neither clearer nor murkier than using offsetof()
(assuming the reader understands offsetof(), of course). It
is neither more nor less secure. It is neither more nor less
maintainable. It is, however, potentially more wasteful, as
illustrated by Cases B and D.
Its much shorter to type, reduces the danger you forget to write the
unneeded offsetof(). You has NOT to think on the extra byte you needs
for the string terminator - because it is already included in the
result of sizeof(struct name).

Shorter to type, yes. It would be shorter still if all the
identifiers were shortened to single letters and all the extra
white space were squeezed out and comments were banned. Personally,
I don't want to work in that world. Brvty's nt alws sol o' wt.

"Reduces the danger you forget to write the unneeded offsetof()"
baffles me. There is, I suppose, some slight danger that you'll
forget to write offsetof(), but I don't see how that differs from
the equally slight danger that you might forget to write sizeof(),
or for that matter malloc(). And offsetof() is "unneeded" in
exactly the same way as sizeof() is "unneeded" -- no matter which
formulation you choose, you've got to include all the parts.

As for remembering to add 1 to strlen(): a C programmer prone
to forgetting the '\0' should be encouraged to seek other avenues
of employment. It's like forgetting the difference between 5/9
and 5.0/9.0 -- a programmer who can't keep such things in mind
with essentially no effort is not a programmer at all.
You can't save the space of a single char because malloc will still
increase the size you requires to be an index next through the next
available aligned address - 1.

The parsing of this sentence is tricky, but I *think* you're
arguing that malloc() will inflate the actual allocation by the
same amount no matter which method is chosen. Well, that's false.
Let's consider your own Case D, here repeated:
D) 4 bytes of int, 4 bytes padding, 1 byte of char[], 7 bytes
padding

sizeof(int) = 4
sizeof(struct) = 16

.... and see what happens with a string of five characters plus a
sixth spot for the terminator:

sizeof(struct name) + strlen(name) == 16 + 5 == 21,
which (by hypothesis) malloc() rounds up to 24.

offsetof(struct name, namestr) + strlen(name) + 1
== 8 + 5 + 1 == 14, which malloc() rounds up to 16.
The only you reaches is to obfuscate
the code.

It's not obfuscated to someone who knows what offsetof() is.
False. Try to determine if 'Mayr' is the surname in an array of 1984
menbers of the sturct by both methods.

/* Code for offsetof() method: */
for (i = 0; i < 1984; ++i) {
if (strcmp(namestructs.namestr, "Mayr") == 0)
return 1;
}
return 0;

/* Code for sizeof() method: */
for (i = 0; i < 1984; ++i) {
if (strcmp(namestructs.namestr, "Mayr") == 0)
return 1;
}
return 0;

I see no important differences between the two code snippets.
But don't use an implementation
defined macro (offsetof is implementation defined), don't define you
own, use only ANSI C to do so.

As requested, I avoided using offsetof(). I also avoided
using pow(), longjmp(), and qsort(), all of which were equally
relevant.

But why avoid offsetof() simply because it's "implementation-
defined?" So, too, is sizeof(). And malloc() and strlen() and
strcmp(), for that matter: the implementation must supply all of
them if it's to conform to the Standard. Why cut yourself off
from a Standard-mandated feature of the language? It's a little
like one of those silly contests to write programs without
semicolons or without curly braces: possibly interesting as an
exercise, but hardly relevant to actual programming.
 

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
474,079
Messages
2,570,574
Members
47,207
Latest member
HelenaCani

Latest Threads

Top