Question

I know that memcmp() cannot be used to compare structs that have not been memset() to 0 because of uninitialized padding. However, in my program I have a struct with a few different types at the start, then several dozen of the same type until the end of the struct. My thought was to manually compare the first few types, then use a memcmp() on the remaining contiguous memory block of same typed members.

My question is, what does the C standard guarantee about structure padding? Can I reliably achieve this on any or all compilers? Does the C standard allow struct padding to be inserted between same type members?

I have implemented my proposed solution, and it seems to work exactly as intended with gcc:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

struct foo
{
    char a;
    void *b;
    int c;
    int d;
    int e;
    int f;
};

static void create_struct(struct foo *p)
{
    p->a = 'a';
    p->b = NULL;
    p->c = 1;
    p->d = 2;
    p->e = 3;
    p->f = 4;
}

static int compare(struct foo *p1, struct foo *p2)
{
    if (p1->a != p2->a)
        return 1;

    if (p1->b != p2->b)
        return 1;

    return
        /* Note the typecasts to char * so we don't get a size in ints. */
        memcmp(
            /* A pointer to the start of the same type members. */
            &(p1->c),
            &(p2->c),
            /* A pointer to the start of the last element to be compared. */
            (char *)&(p2->f)
            /* Plus its size to compare until the end of the last element. */
            +sizeof(p2->f)
            /* Minus the first element, so only c..f are compared. */
            -(char *)&(p2->c)
        ) != 0;
}

int main(int argc, char **argv)
{
    struct foo *p1, *p2;
    int ret;

    /* The loop is to ensure there isn't a fluke with uninitialized padding
     * being the same.
     */
    do
    {
        p1 = malloc(sizeof(struct foo));
        p2 = malloc(sizeof(struct foo));

        create_struct(p1);
        create_struct(p2);

        ret = compare(p1, p2);

        free(p1);
        free(p2);

        if (ret)
            puts("no match");
        else
            puts("match");
    }
    while (!ret);

    return 0;
}
Was it helpful?

Solution

There is no guarantee of this in the C standard. From a practical standpoint it's true as part of the ABI for every current C implementation, and there seems to be no purpose in adding padding (e.g. it could not be used for checking against buffer overflows, since a conforming program is permitted to write to the padding). But strictly speaking it's not "portable".

OTHER TIPS

Sadly, there is no C standard (that I have ever heard of) that allows you to control structure padding. There is the fact that automatic allocation that is initialized like this

struct something val = { 0 };

will cause all the members in val to be initialized to 0. But the padding in between is left to the implementation.

There are compiler extensions you can use like GCC's __attribute__((packed)) to eliminate most if not all structure padding, but aside from that you may be at a loss.

I also know that without major optimizations in place, most compilers won't bother to add structure padding in most cases, which would explain why this works under GCC.

That said, if your structure members cause odd alignment issues like this

struct something { char onebyte; int fourbyte; }; 

they will cause the compiler to add padding after the onebyte member to satisfy the alignment requirements of the fourbyte member.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top