Live data from Hacker News

Structures in C: From Basics to Memory Alignment

abstractexpr.com

41–50 of 58 posts

Re: Structures in C: From Basics to Memory Alignment

#41
post #36

> But what if we want a struct to feel like a real C type? Remove this; it's already a real type. You could go over the two namespaces and why you need to type 'struct' otherwise, but I wouldn't present a false dichotomy of real and "unreal" types. > Declaring a Struct as a New Type It's not really a new type. It's a type alias, and the two names can be used interchangeably. I would just call it a type alias. https:/…

{0} is standard C. {} is currently a (common) compiler extension but will be standard C23: https://open-std.org/JTC1/SC22/WG14/www/docs/n2900.htm

Yes, it's a bit frustrating, especially for headers with inline/macro code. And for headers, requiring C23 doesn't seem sensible for quite some time. I define a macro:

    #ifdef __cplusplus
    # define ZERO_INIT {}
    #else
    # define ZERO_INIT {0}
    #endif
Works for arrays, aggregates, scalars, etc., but I just use it for arrays and aggregates: `char buf[32] = ZERO_INIT; struct X x = ZERO_INIT;`

Re: Structures in C: From Basics to Memory Alignment

#42

Good article, a couple nits/additional notes: 1. The article points out you should compare structs field by field, but it doesn't explain why memcmp wouldn't work. The reason is that the padding between the fields might not necessarily be zeroed in all cases. Field by field comparison is resilient to this. 2. The article proposes this for dynamic allocation: struct Vector2D *vec = malloc(sizeof(struct Vector2D)); I t…

I’ve been wondering about (1) recently—is there a way to memset the entire stack frame at the start of a function such that memcmp works as expected? Also, what are the performance implications of comparing a padded struct member-by-member vs a single big memcmp? Is member-by-member faster because you’re comparing less in total, or is memcmp faster because it’s one big contiguous compare? Or is it more complicated? R…

> is there a way to memset the entire stack frame at the start of a function such that memcmp works as expected?

I'd argue no simply because the value of the padding bytes is always unspecified. A compiler that sees such a `memset` is (IMO) perfectly free to not zero known padding bytes since it knows their value should not matter to the program. Compilers might not currently do that but you can already see this kind of behavior in other situations - C compilers will happily throw out `memset` calls if it knows the result won't be used.

But also beyond that, it probably doesn't matter anyway because there's no way to _use_ the `struct` which won't leave the padding bytes with unspecified values. `memset` might reliably zero the padding bytes for you, but writing to the struct will randomly screw up the padding bytes depending on what the compiler feels is the best way to do things, so then you're back at `memcmp` no longer working. The only real way to make `memcmp` work is to ensure you have no padding bytes to begin with.

Re: Structures in C: From Basics to Memory Alignment

#43

> The only good reason to use packed structures is when you need to map some memory (e.g. hardware registers exposed to memory) bit by bit to a structure. Another common reason is when two CPUs of different architecture need to access the same structure in memory. E.g. you have a RiscV and an Arm64 processor in the same system, sharing memory. Or you read structured binary data from disk and need to specify an exact…

Whoa, I didn’t know there were systems where two different CPUs shared memory… How does that work? Any examples I might recognize of such systems?

It is common for small CPUs to be embedded inside of various hardware devices. For instance, your GPU might have one or more control CPUs embedded inside. These CPUs would have either direct or at least DMA access to main memory. If you have heard about "firmware" being necessary for a hardware device to function, that "firmware" is really just software that runs on one of these auxiliary CPUs.

Suppose you want to send a command from the main CPU to this subprocessor. For efficiency and simplicity, that command might be defined as a C struct, in a common header. In that case it can be good to use packed alignment so you don't have to worry about possible layout differences between CPUs.

Re: Structures in C: From Basics to Memory Alignment

#44

Earlier quoted context omitted.

>but pointers inside struct is more 'flexible', for example you can put a 'void *' and cast it to various data types. But then the array and other members won't be next to each other in memory.

the cache subsystem might be smart enough to cache the first line, e.g. the size of the array etc, then pull the rest of array content from heap and cache the array as needed, should be fine to me, yes two cache misses instead of one at the beginning.

The CPU cache simply pulls the memory next to what you loaded. It doesn't interpret what it sees.

Re: Structures in C: From Basics to Memory Alignment

#45

> The only good reason to use packed structures is when you need to map some memory (e.g. hardware registers exposed to memory) bit by bit to a structure. Another common reason is when two CPUs of different architecture need to access the same structure in memory. E.g. you have a RiscV and an Arm64 processor in the same system, sharing memory. Or you read structured binary data from disk and need to specify an exact…

Or you have to transmit a struct as a network packet "as is", between different architectures.

Re: Structures in C: From Basics to Memory Alignment

#46

Earlier quoted context omitted.

I’ve been wondering about (1) recently—is there a way to memset the entire stack frame at the start of a function such that memcmp works as expected? Also, what are the performance implications of comparing a padded struct member-by-member vs a single big memcmp? Is member-by-member faster because you’re comparing less in total, or is memcmp faster because it’s one big contiguous compare? Or is it more complicated? R…

> is there a way to memset the entire stack frame at the start of a function such that memcmp works as expected? I'd argue no simply because the value of the padding bytes is always unspecified. A compiler that sees such a `memset` is (IMO) perfectly free to not zero known padding bytes since it knows their value should not matter to the program. Compilers might not currently do that but you can already see this kind…

Compiles are free to not zero padding bytes if a struct is passed to memset but there are some situations where padding is not unspecified, for example if you do partial initialization of it.

Re: Structures in C: From Basics to Memory Alignment

#47

Earlier quoted context omitted.

> The phrase "natural aligned" has no meaning in that context. The phrase “naturally aligned” as I’m accustomed to seeing it used refers to the alignment of a power-of-two-sized type (usually a scalar one) being equal to its size. Unless you’re working with, say, 18-bit or 24-bit integers (that do exist in obscure places), it does have a meaning, and unless you’re using non-eight-bit bytes that meaning is fairly univ…

> the default ABI settings should get you completely compatible layouts here That's not true! You must not assume that the alignment always equals the size of a type. For example, the SysV i386 ABI uses 32-bit alignment for 64-bit types (double, int64_t). The Microsoft x86 ABI, however, uses 64-bit alignment, as do all 64-bit ABIs (See https://stackoverflow.com/a/11110283 .) If you want to share structs directly betw…

What's worse, MSVC's 32-bit x86 ABI reports an 8-byte alignment requirement (via __alignof) for 64-bit integer types, and its struct layout algorithm uses that alignment to determine padding, but those integers and structs are only aligned to 4 bytes when allocated on the stack! This has caused issues with Rust code trying to link with MSVC code [0], since Rust's standard library documentation asserts that properly aligned pointers have addresses that are always a multiple of the alignment used for struct layout.

[0] https://github.com/rust-lang/rust/issues/112480

Re: Structures in C: From Basics to Memory Alignment

#48
post #26

Earlier quoted context omitted.

I think we agree that this makes sense on some metaphysical level. The problem is that there are definitely platforms where the normal alignment isn't what you describe above. And there isn't to my knowledge a switch in GCC to force it to follow these rules on any given platform. There isn't __attribute__((natural_alignment)). But there is __attribute__((packed)).

Since C11 there is _Alignas(sizeof T), forcing one of the proposed meanings for alignment, and _Alignof(T), which queries actual (i.e. natural, per another meaning) alignment. But, yeah, the argument upthread seems more about the implicit meaning of natural than anything else.

On that note, something that caught me off guard once is that C11 _Alignof and GCC __alignof__ can differ: for example in 32-bit x86 __alignof__(double) == 8 but _Alignof(double) == 4; however __alignof__(struct { double d; }) == 4. Apparently __alignof__ gives the preferred alignment whereas _Alignof gives the alignment required by ABI.

Re: Structures in C: From Basics to Memory Alignment

#49

Good article, a couple nits/additional notes: 1. The article points out you should compare structs field by field, but it doesn't explain why memcmp wouldn't work. The reason is that the padding between the fields might not necessarily be zeroed in all cases. Field by field comparison is resilient to this. 2. The article proposes this for dynamic allocation: struct Vector2D *vec = malloc(sizeof(struct Vector2D)); I t…

I’ve been wondering about (1) recently—is there a way to memset the entire stack frame at the start of a function such that memcmp works as expected? Also, what are the performance implications of comparing a padded struct member-by-member vs a single big memcmp? Is member-by-member faster because you’re comparing less in total, or is memcmp faster because it’s one big contiguous compare? Or is it more complicated? R…

If you really wanted to memcmp a padded struct, you could declare it as packed and define the padding yourself.

Re: Structures in C: From Basics to Memory Alignment

#50
post #19

> The only good reason to use packed structures is when you need to map some memory (e.g. hardware registers exposed to memory) bit by bit to a structure. Another common reason is when two CPUs of different architecture need to access the same structure in memory. E.g. you have a RiscV and an Arm64 processor in the same system, sharing memory. Or you read structured binary data from disk and need to specify an exact…

"The only good reason to " The word opinionated was coined and adopted in English to describe a certain attitude. It has functioned fine for (probably) centuries (who knows, and I can't be bothered to research too far). Then came the age of IT and blow me, are we not opinionated to the point of ridiculousness. A sentence construction along the lines of "The only good reason to" [do x] "is" [y], seems to invite a nega…

>I'm pretty sure packed structures have other uses.

And if you want to learn of them, there is no faster way.

Post reply on HN