struct foo {
int bar;
}
void myfunc(struct foo *p) {
int *t = &p->bar;
if(p == NULL) return;
*t = 5;
}
Is this undefined? Probably. Absent compiler optimizations, it won't cause a crash on any system you're ever likely to encounter because it's not actually dereferencing the pointer, but some versions of gcc have caused security issues in Linux by optimising away NULL checks like this. ...
void myfunc(struct foo *p) {
int *t = (int *)((char *)p + offsetof(p->bar));
...
}
right? In the C abstract machine taking the address of an offset of a pointer is always defined, I think.This is true as dereferencing a NULL pointer is undefined behaviour, so is fair game for optimizations. The compiler may assume that NULL pointer dereferencing results in a trap/segfault and that the conditional can never be executed.
If I am wrong, and your example actually exhibits undefined behaviour, I would certainly love to learn why.
Edit: I'm a moron, thanks for pointing out the obvious to me :)
Of course if you pass a pointer to a type smaller than sizeof(int)-1 bytes then it accesses uninitialized memory which is UB, but that's a different issue.
EDIT: Aaaand I wasn't thinking.
̶E̶D̶I̶T̶ ̶E̶D̶I̶T̶:̶ ̶N̶o̶t̶ ̶o̶n̶l̶y̶ ̶d̶i̶d̶ ̶I̶ ̶m̶i̶s̶-̶r̶e̶a̶d̶,̶ ̶b̶u̶t̶ ̶v̶o̶i̶d̶ ̶p̶o̶i̶n̶t̶e̶r̶ ̶c̶a̶n̶'̶t̶ ̶a̶l̶i̶a̶s̶ ̶a̶n̶y̶t̶h̶i̶n̶g̶ ̶i̶n̶ ̶s̶t̶a̶n̶d̶a̶r̶d̶ ̶C̶ ̶a̶n̶y̶w̶a̶y̶ ̶(̶t̶h̶o̶u̶g̶h̶ ̶I̶I̶R̶C̶ ̶G̶C̶C̶ ̶t̶r̶e̶a̶t̶s̶ ̶i̶t̶ ̶l̶i̶k̶e̶ ̶c̶h̶a̶r̶ ̶p̶o̶i̶n̶t̶e̶r̶ ̶w̶r̶t̶ ̶a̶l̶i̶a̶s̶i̶n̶g̶)̶.
EDIT EDIT EDIT: Actually maybe it can and I shouldn't be reading technical docs at 2am.
You should read about strict aliasing, you will be surprised.
Somehow I wasn't thinking and didn't realize that `t` aliases whatever was passed as `p`, and dereferencing a non int or char pointer would be undefined there.
Consider this mini example:
Accessing p before null check is undefined, and causes compilers to deduce that the null check isn't required. It can therefore be stripped, which most programmers would find very surprising.Edit: yes, the replies are right, I meant to dereference *p rather than just copy the pointer. Fixed and somewhat simplified.
I was trying (badly) to remember this example: http://blog.llvm.org/2011/05/what-every-c-programmer-should-...