4 ms·
You got so worked up about specific examples that you missed the point somewhat. Consider this mini example: void myfunc (int * p) { int t = *p;
by kibibu 11y ago
You got so worked up about specific examples that you missed the point somewhat.
Consider this mini example:
void myfunc (int * p) {
int t = *p;
if(p == NULL) { return;}
*p = t + 1;
}
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-know_14.html http://blog.llvm.org/2011/05/what-every-c-programmer-should-...
- vrfcodf 11y agoThat code is well defined.
- umanwizard 11y agoDoesn't *t = 1 invoke UB if t is null? Edit: I'm a moron, thanks for pointing out the obvious to me :)
- PeCaN 11y agoYes, but note that the assignment occurs after the NULL check. If the grandparent did mean to put it before, then the code snippet would be undefined, but it wouldn't be surprising either.
- vrfcodf 11y agoI guess one could still be evil and pass a pointer to a non-int type to the function. That example is really bad, you don't need a void pointer to show this ub 'optimization'.
- PeCaN 11y agoThat'd still be defined though. It'd just be defined to set the first sizeof(int) bytes of `* p` to 1. ;) void * and (char * ) can alias to anything and the compiler has to make it work. 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.
- vrfcodf 11y agoThat is not correct. The type used to write to the memory is int, not void or char. If the object, whose address is passed to the function is not compatible with int, the behavior is undefined. You should read about strict aliasing, you will be surprised.
- PeCaN 11y agoYou're right, I misread and misinterpreted that! Thanks. 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.
- orf 11y agoOn Firefox your edit strikethrough makes the page very very wide.
- quotemstr 11y agoMaybe you should familiarize yourself with the standard before preaching about how easy it is to comply with it.
- xyzzy_plugh 11y agoI'm not sure this is correct. I think you are referring to the fact that if p was dereferenced before the NULL-check, then the compiler could optimize out the entire conditional statement. 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.
- kibibu 11y agoNope, you're right - I meant to dereference the pointer, rather than just access it. D'oh!
- eps 11y agoNo compiler will optimize away the NULL check here. Your example is plain wrong.
- deleted 11y ago[deleted]
- makomk 11y agoOne that often gets people is this: 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.
- ryanprichard 11y agoFWIW, the tis-interpreter doesn't flag that code (aside from the missing semicolon). I think you're right, though, that it has UB.
- PeCaN 11y agoI'm not sure that's undefined though. It's equivalent to ... 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.
- deleted 11y ago[deleted]
- __s 11y agoIt is not always defined. It's defined to take an address to a pointer with an offset within the object's space plus one. Subtracting two pointers to distinct objects is undefined. This is to allow for segmented memory architectures. It's also to allow implementation of garbage collectors by the compiler