Nytro Posted November 6, 2011 Report Posted November 6, 2011 How Structs Really Work in CPosted by paul16 October 2011You don't know it yet, but you're...wrong.I'm not even going bother asking the question. Wouldn't be any point, other than to embarass you and make me look douchey...and I look douchey enough as it is.So let's just drop it --Wait, you still want to hear the question?Seriously?Well fine. Since you're so smart, riddle me this: what's the purpose of a struct in C?Oh, what's that you say? The purpose of a struct is blah de blah blah container blah fields blah data type blah de blah blah blah blah?Yeah, guess what? You're wrong.You want to know the true purpose of a struct in C? The purpose of a struct is to confuse the hell out of programmers who've forgotten, or never learned in the first place, that in C, memory is king. How is memory king? Well, let us see...1) In C, a struct maps to memory Let's say you want to do something with a video file. In memory, the file looks like this: It uses two bytes for the header, one byte for the video type, four bytes for the size, and 1024 bytes for the video data. Pretty easy to see how this will map to a struct, right? struct video_file{ short header; char type; int size; char data[1024]; }; You use memcpy() or a direct pointer assignment to get access to it: struct video_file v; memcpy(&v, video_loc_in_memory, sizeof(struct video_file)); size = v.size; /* or whatever */ ... struct video_file * p = video_loc_in_memory; size = p->size; /* or whatever */ Not too bad, right? Well, here's the problem: if padding bytes are involved, you could be entering a world of pain and not even know it. Many compilers insert padding bytes into the struct to ensure the fields are byte-aligned. What's worse, they often do this by default. If that happens, you could end up with a struct that, internally, looks like this: struct video_file{ char padding; /*what?!?!*/ short header; char type; char padding; /*what?!?!*/ int size; char data[1024]; }; So instead of 1031 bytes, you've got a struct that's 1033 bytes long. Surprise! Try doing a memcpy() now and see what happens! The solution is to use a compiler switch or a #pragma pack to ensure that structs are packed (i.e., no padding applied).2) In C, structs are allowed to "run off the end" In reality, video files aren't statically sized (and they contain a hell of a lot more data than 1024 bytes!). So let's rewrite our struct in a way that's guaranteed to give a newb his or her daily dose of WTF: struct video_file2{ short header; char type; int size; char data[0]; }; A zero length array? Well, just remember: memory is king. As long as a symbol is backed by real memory, you can do what you want with it -- including running it past its boundaries (you will never see an OutOfBounds exception in C!) Example: This video uses two bytes for the header, one byte for the video type, four bytes for the size, and n bytes for the video data. This technique requires a struct pointer: struct video_file2 * p = video_loc_in_memory; for(int i=0; i<p->size; i++) p->data[i] = (char)(p->data[i] * 0.5); /* apply filter, maybe */ Note: some compilers don't allow zero-length arrays. In that case, you can use a 1-element array. Same principle applies: just overrun the end of it to suit your needs.3) In C, you can compute an offset within a struct by committing an offense against aesthetics so grave it makes the cast of Jersey Shore look positively Fraiserish in comparison. struct s{ short x; char y; char z[100]; short thing; }; You need to get the offset of thing inside of s. What's the conventional way to go about this? Well, duh, like this: size_t offset = (size_t) &(((struct s*)0)->thing); In the immortal words of Arnold, "That's one...ugly...mudder...fu--" Yes, it's pretty gross, but remember, in C, memory is king. Pretend you've got a struct s at memory location zero. If you map it out, it looks like this: Because the struct is based at zero, the address of thing is its offset! In this case, 103 bytes. Except there's one problem, isn't there? How in the world are you supposed to dereference memory address zero? That's, like, a memory access violation waiting to happen! Well, actually...that's a trick question. Nothing gets dereferenced! (struct s*)0 casts zero to a struct s pointer. No dereferencing occurs. ((struct s*)0)->thing informs the compiler that you will be doing something with thing, but nothing happens with it at that point. No dereferencing occurs. &(((struct s*)0->thing) computes the address of thing using pointer arithmetic. It doesn't actually touch the memory, so no dereferencing occurs. Note: officially, you're supposed to use the offsetof() function to do this, but if you ever do any real C programming (i.e., embedded) you'll see this in other people's code, so you might as well get comfortable with how it works. Conclusion:Well, that was a brief rundown of how C structs interact with memory and vice-versa. I hope you found it useful, and if you have any questions, feel free to leave them in the comments.Sursa: How Structs Really Work in C | TENACIOUS C Quote