← Back to context

Comment by blastrat

10 years ago

I knew C quite well. Haven't written any for years. The statements "zero all bytes of a struct" and "reads of the padding" contain enough ambiguity that it answers the question. Not to mention the ambiguity in the words "read" and "write" as they pertain to C, since they already have a "std" meaning that's not the same as lvalue or rvalue, so what exactly do they mean here?

And if you think you can answer the question without resolving the ambiguities, that answers some other questions.

I believe the questions in this study (I did not write it, only know the authors) were deliberately open-ended, allowing for comments on the specifics. A previous, much longer version used to contain code examples to comment, but it proved too detailed for people to complete.

Moreover, the study was explicitly not about ISO C: "We were not asking what the ISO C standard permits, which is often more restrictive, or about obsolete or obscure hardware or compilers. We focussed on the behaviour of memory and pointers. This is a step towards an unambiguous and mathematically precise definition of the de facto standards: the C dialects that are actually used by systems programmers and implemented by mainstream compilers."

Here is an actual example of a comment to this question:

    I would expect this code to work:
    
    struct foo
    {
        char a;
        double b;
    };
    
    foo p;
    foo q;
    memset( &p, 0, sizeof( p ) );
    memset( &q, 0, sizeof( q ) );
    p.a = 1;
    q.a = 1;
    assert( memcmp( &p, &q, sizeof( foo ) ) == 0 );

  • Indeed. The long version is at [pdf] http://www.cl.cam.ac.uk/~pes20/cerberus/notes30-full.pdf; it has 85 questions supported by concrete code examples and experimental data, e.g. (one of several questions that refine the above):

    Q64. After an explicit write of zero to a padding

    byte followed by a write to adjacent members of

    the structure, does the padding byte hold a

    well-defined zero value? (not an unspecified

    value)

      #include <stdio.h>
    
      #include <stddef.h>
    
      typedef struct { char c; float f; int i; } st;
    
      int main() {
    
        // check there is a padding byte between c and f
    
        size_t offset_padding = offsetof(st,c)+sizeof(char);
    
        if (offsetof(st,f)>offset_padding) {
    
            st s; 
    
            unsigned char *p = 
    
              ((unsigned char*)(&s)) + offset_padding;
    
            *p = 0;
    
            s.c = 'A';
    
            s.f = 1.0;
    
            s.i = 42;
    
            unsigned char c3 = *p; 
    
            // does c3 hold 0, not an unspecified value?
    
            printf("c3=0x%x\n",c3);
    
        }
    
        return 0;
    
      }
    

    Some of the questions have clear answers with respect to either the ISO or de facto standards, but many do not - that's the point.

  • Is memset and memcmp compatible with strict aliasing? Intuitively it seems like that would be a gap into the aliasing rules. Altough void* is allowed to alias anything so maybe it works through that. Ive never seen memset, memcpy or memcmp on anything but char* in production code.

    • The aliasing rules only talk about dereferencing pointers. Void* can't be dereferenced so it has no interaction with the aliasing rules. You might be thinking about char, and yes, you are allowed to dereference char to inspect the bytes of an object, which is what the various mem* functions do under the hood.