10 ms·
Agreed, that's not undefined behavior. It's just not guaranteed to 0 out y
by hackcasual 7y ago
Agreed, that's not undefined behavior. It's just not guaranteed to 0 out y
- tumult 7y agoIt actually would be guaranteed to 0 it if it were instead unsigned chars. For plain char, the implementation has to specify if it is signed, and if it is, what kind of padding it has. If it has none, it would also be guaranteed. Therefore, you can just check the compiler manual to see if this guarantees zeroing. (None of the mainstream compilers on mainstream platforms will have padding for signed char, either.) (C99: §6.2.5 and §6.2.6.2) (If you know a platform where this is not true, I'd be interested in hearing about it!) Edit: Oh, also, an implementation is allowed to have different padding in struct fields than the padding of the type itself. But it has to define this, so you would be able to look it up in the manual to see if it's different. (§6.7.2.1: "Each non-bit-field member of a structure or union object is aligned in an implementation-defined manner appropriate to its type.")
- quelsolaar 7y agoThis is my point, No platform I know of puts padding between bytes, so the behavior should be clear, but since a platform CAN put padding between bytes, and writing to said padding is UB, some compiler designers thinks this a license to do what ever.
- tumult 7y agoIt's not. Writing to the padding is always allowed, on every platform. [1] As for whether or not every field will be zeroed in the example, and whether or not you can accidentally generate a trap representation by writing to this padding, each implementation of C99 (platform/compiler/whatever) must specify somewhere what the padding for chars in a struct is. You can look it up from the manual, or whatever documentation is provided. If this makes you scared about your software being built on an unknown platform and where you need to zero every field of the struct in the fashion shown in the example, then you can just keep a list of the implementations where you know it's safe in the readme, or have the build system stop if it's not on the approved list where you've read the manual for that implementation. You can go ahead and fill in x86/AMD64/AArch64 for gcc/clang/msvc, because it will be fine for those. Also Power and other common stuff. [1]: If you have an example of where it's not, I would be very interested in hearing about it!
- quelsolaar 7y agoSorry, You are right. writing is allowed, but assuming padding is not. Yes, it should work on all the mentioned platforms, and that's my point. It should be possible to write code that assumes there is no padding on the platform you use. Some C compilers see it differently, because they think that since padding is not defined by the C standard they can do what ever they want even on platforms where it is defined.
- tumult 7y agoSorry to nitpick, I hope this isn't too annoying -- do you have an example of an implementation (platform, compiler, whatever) where this statement is true? Some C compilers see it differently, because they think that since padding is not defined by the C standard they can do what ever they want even on platforms where it is defined. I am genuinely interested to know of one like that. It wouldn't be a very useful C99 compiler, because C99 says that it is OK to do this, and lots of C99 code does this. But I would like to know about the existence of an implementation like this for use as an example in the future when talking about this topic.
- comex 7y ago> Some C compilers see it differently, because they think that since padding is not defined by the C standard they can do what ever they want even on platforms where it is defined. No, they don't. Implementation-defined behavior means that the behavior on any given implementation should be defined, and you are allowed to depend on that behavior being what it is. You are probably thinking of something like integer overflow or unaligned pointer access, where the historical justification for it not being allowed is based on differences between architectures, and thus it could hypothetically have been made implementation-defined behavior. But the spec chose to make it undefined behavior, which is why compilers can aggressively optimize assuming it won't happen, even on architectures where the 'obvious' assembly translation has some known behavior. However, that does not apply to padding. There is a type of potential undefined behavior involved in the example: if padding exists, then the memset does not cover y, so y is left as uninitialized, and if you then read from y (not included in the example) you would get UB. [1] But whether padding exists is implementation-defined, so if you know your implementation does not put padding there, you can safely read from y. As far as I know, all C compilers in common use respect this. [1] https://stackoverflow.com/questions/1597405/what-happens-to-a-declared-uninitialized-variable-in-c-does-it-have-a-value https://stackoverflow.com/questions/1597405/what-happens-to-...
- catblast 7y ago> memset(&a, 0, sizeof(char) * 2); The example is sizeof(char)*2, not sizeof(a). If there is any padding at all the example as given will not guarantee zeroing out everything. Not sure what that has to do with unsigned vs signed.
- tumult 7y agoC99 makes a specific distinction about unsigned char not having any padding in its object representation. It doesn't make this distinction for signed char or plain char (because plain char might be signed char.) In practice, on all major implementations that I know of, both signed and unsigned char have no padding. You might have misunderstood what I wrote. I was saying > memset(&a, 0, sizeof(char) * 2); can be guaranteed to be OK for reading (in addition to writing) if you check the manual of your implementation. C99 says the relevant padding/alignment rules need to be specified or documented somewhere. Of course, memset(..., sizeof a) will be fine, too, without having to check the manual to see what the padding/alignment rules are.
- catblast 7y agoIn the just previous post: > It actually would be guaranteed to 0 it if it were instead unsigned chars. and bringing up padding. But this is padding within the char, not padding within the struct. It is because of the latter, that sum(sizeof(members)) is not necessarily == to sizeof(struct). The sizeof(char) and sizeof(unsigned char) are both defined as 1, and of course the bit size of an unsigned char including padding is CHAR_BIT. And it turns out because of a char* must be able to access at least every accessible char of every other object, and have the weakest alignment that alignof(char) pretty much has to be 1 except in a contrived example http://port70.net/~nsz/c/c11/n1570.html#6.2.8 http://port70.net/~nsz/c/c11/n1570.html#6.2.8. Further, although not required, struct member padding will be a direct result of alignof, hence you will pretty much never find a wild example where sizeof(char)*2 == 2 != sizeof(struct a)... so maybe it is fair to call it a guarantee?? But, still, I think the way you're saying it just confuses the alignment/padding issue further.
- 7y ago
- cygx 7y agoIt's just not guaranteed to 0 out y Citation needed. As far as I'm aware, memsetting structs is perfectly fine. The problem are things like memcmp as padding bytes take unspecified value.
- aw1621107 7y agoI believe y may not be zeroed out if there is padding between x and y. Now, whether any actual compiler would insert padding there, I have no idea.
- Gibbon1 7y agoI do memory twiddling a lot in C. The answer is what happens in practice depends on the machine alignment. On an 8 bit machine generally alignment is 1 byte and there is no padding. On a 32 bit machine yeah you'll have 3 bytes of padding for each element. Me I think alignment is increasingly a bad idea. Most modern processors don't deal with memory in word sized chunks anymore. Padding just increases cache pressure. Either way the example is trashfire grade C.
- tumult 7y agoThat is not how padding and alignment works in C. The example above has no padding between each field, because the implementation specifies how much padding there should be. For clang, gcc, and msvc, on x86 and AMD64, there is no padding between chars in a struct. if the struct were instead { char a; int b; } then there would be 3 bytes of padding between a and b. The example is not trashfire and is in fact totally valid. Common ARM processors will fault if you try to perform an unaligned access. You must align where required. Edit: correction to mistake above: I originally wrote { int a; char b; } and should have said the padding came after b. I've fixed it to match the explanation text and have the padding between. I should really be writing these posts in a separate text editor.
- Const-me 7y ago> if the struct were instead { int a; char b; } then there would be 3 bytes of padding between a and b. I doubt it. To get 3 bytes of padding between fields, you need this: { char a; int b; }