tags:

views:

206

answers:

7

this is my struct :

struct Node {
    struct Node* data;
    struct Node* links[4];
}

assuming there is no padding, does Node->links[-1] guaranteed to be pointing on Node::data ?

+2  A: 

I'd say yes, but why then don't declare it as

struct Node {
    struct Node* links[5];
}
ruslik
because in my case, i can't do that.
uray
A: 

I think it is. But it's not guaranteed by the standard that there is no padding.

Edit: but the standard also says that behavior is undefined if "An array subscript is out of range, even if an object is apparently accessible with the given subscript".

+13  A: 

No guarantee; this is undefined behaviour:

  • Compiler-dependent structure padding
  • Standard only defines array indexing between 0 and length (inclusive)
  • Possible strict-aliasing violation

In practice, it's quite possible that you will end up pointing at data, but any attempts to access it will result in UB.

Oli Charlesworth
[1]my question is assumed that there is no padding, and you can set compiler to produce no padding.[2]then about the standard can you refer me which line it said that? because my understanding is for array `a[]`, the index `i` is defined as `*(a+i)` which is the same as `*(i+a)` for `a[i]` or `i[a]`. [3]what is strict-aliasing violation?
uray
For pointer arithmetic, see C99 section 6.5.6, paragraph 8. I'm mistaken on strict-aliasing, it doesn't apply in this case (so I'll edit my answer).
Oli Charlesworth
Strictly speaking, this results in implementation defined behavior, not undefined behavior -- the implementation dependency is due to the structure padding.
Chris Dodd
The standard defines array indexing in terms of pointer addition. The result of a pointer addition is only defined as long as it results in an address within the allocated object or immediately after it.
Chris Dodd
@Chris: The behaviour is undefined. See Annex J.3 for things that are implementation-defined. In practice, the behaviour may be non-deterministic because the compiler will make certain optimisations on the assumption that `data` won't be accessed in this manner.
Oli Charlesworth
The answer you're probably looking for: yes, this will work on pretty much every C compiler. It just isn't promised to work. :-)
xscott
@xscott: I really wouldn't rely on it! I've been bitten by aggressive compiler optimisations too many times when it comes to e.g. strict-aliasing violations, and this is similar.
Oli Charlesworth
@Oli Charlesworth: Show me *any* C compiler where this exact thing doesn't work. Your comment about strict-aliasing might apply if he passes overlapping pointers to this structure in to a function with strict arguments, but I don't think that is similar at all.
xscott
@xscott: I don't have an example! But then I didn't have an example of strict-aliasing violations breaking things until it happened! IMHO, this is very similar; it's about the compiler being allowed to make assumptions about aliases. The standard allows it to assume that `links` can only be used to access `links[0]` to `links[3]`. If you use it to access `links[-1]`, then you have *absolutely* no guarantee that this won't break if you increase the optimisation setting, where the compiler aggressively reorders accesses to variables. This is nothing to do with `restrict` function args.
Oli Charlesworth
+3  A: 

A union might help you here. Something like this:

struct Node {
  union {
    Node *data;
    struct {
      Node *lnk[5];
    } links;
  }
}
Keith Randall
thank you, that helps..
uray
This will probably work in practice, but note that writing to one enum member and then reading from another technically causes undefined behavior.
FredOverflow
A: 

Almost Yes

Yes, it almost certainly will point at data, but it may not be precisely a guaranty.

You actually do get a guaranty that x[-1] is the same thing as *(x - 1), so, depending on what you put at the address of x - 1, then you do have a sort of guaranty. (And don't forget that when subtracting from pointers, the decrement is by the size of the item.)

Although others have objected on the basis of strict aliasing, in this case the types are the same so the elaborate optimizations should not remove your expected behavior.

DigitalRoss
A: 

This is implementation defined, depending on how the implementation pads structures and arrays. However, as long as it pads structs and arrays the same way, it should work.

Chris Dodd
See comments to my answer for why this is *undefined*, not *implementation-defined*.
Oli Charlesworth
+6  A: 

Array subscripting is defined in terms of pointer arithmetic, and the C99 standard has this to say about pointer arithmetic:

If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

So accessing Node->links[-1] (even just getting the address of Node->links[-1]) is undefined behavior, strictly speaking. So you have no guarantee that Node->links[-1] will get you Node::data.

But as many commentators mention, it will work pretty much always. I'd still consider it poor programming practice that should be avoided. If not for the technicality, then because it requires that modifications to struct Node can easily cause bugs that the compiler will not help you with. When someone adds something between data and links, things will mysteriously break.

Michael Burr
+1 for actually coming up for a good reason why it's poor programming practice (imho `undefined behaviour` is a little short as explanation, several cases of ub in the standard are only put there to allow for truely exotic hardware and are in practice not important).
tristopia