0

I recently came across some code that uses a "cool hack" like the following:

#include <stddef.h>
#include <stdlib.h>

struct foo {
    int a;
    char *b;
    int optional;
    char huge[5000];
    /* lots more members */
};

void myfunc(void) {
    struct foo *p;
    p = malloc(offsetof(struct foo, optional));
    if (p) {
        p->a = 17;
        p->b = "Hello";
        /* do stuff with p->a and p->b */
        free(p);
    }
}

The idea is to "save memory" by only allocating the part of struct foo that is actually going to be used.

I see many obvious reasons why this code is a bad idea: it will certainly break if you reorder the members of struct foo, or if you forget which members you're allowed to use, or if you accidentally assign *p or pass it to a function by value. (And in the code linked, it saves a whopping 80 bytes per call, for a function that most programs will only call a handful of times.)

But is it actually undefined behavior or otherwise illegal with respect to the C standard? If so, are there examples of real-life implementations where it won't work as intended?

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
  • 1
    Dup: [Allocating memory for a part of structure](https://stackoverflow.com/q/53904602/1275169) – P.P Feb 23 '20 at 22:52
  • 1
    @P.P: Thanks, I don't know how I didn't find that. I've voted to close mine. – Nate Eldredge Feb 23 '20 at 22:57
  • For a real-life example where it won't work, see https://stackoverflow.com/questions/46522451/why-is-gcc-allowed-to-speculatively-load-from-a-struct – Nate Eldredge Feb 21 '21 at 06:29
  • Incidentally, the glibc code linked in the question has [since been changed](https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=7a887dd537cd00fe3cdf42b788b3f0e3b430b0ed) to not use this technique anymore. – Nate Eldredge Feb 21 '21 at 06:41

1 Answers1

2

I don't think the Standard made any effort to consider this pattern. It was certainly in common use when the Standard was written, and supported by all commonplace implementations. While there was no obvious reason why any general-purpose implementations wouldn't support it, I don't know that the authors of the Standard wanted to preclude the possibility of implementations whose customers wouldn't need this pattern, squawking if they noticed allocations' addresses assigned to pointer-to-structure types that were too big for them. Instead, I think they expected implementations to process such constructs usefully absent a good reason to do otherwise, but didn't want to speculate what "good reasons" might be.

As to the question of whether such code will work reliably, there's no particular reason why it shouldn't, and I don't know of any particular situations where it would cause problems with clang or gcc, but present or future versions might find some "clever" optimizations where it would fail.

supercat
  • 77,689
  • 9
  • 166
  • 211