question about 2.6 in the faq

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • myheartinamerica

    question about 2.6 in the faq

    I was confused about part 2.6 in the comp.lang.c. FAQ.

    2.6 ... <cut>
    Another possibility is to declare the variable-size element very
    large, rather than very small; in the case of the above example:

    ...
    char namestr[MAXSIZE];

    where MAXSIZE is larger than any name which will be stored.
    However, it looks like this technique is disallowed by a strict
    interpretation of the Standard as well. Furthermore, either of
    these "chummy" structures must be used with care, since the
    programmer knows more about their size than the compiler does.


    I thought it was valid to have arrays of characters in a struct. Am I
    missing
    something? I realize the intent of the FAQ was to explain how not to
    simulate
    variable-length arrays.

    Thanks for any explanation,
    Mick


  • vippstar@gmail.com

    #2
    Re: question about 2.6 in the faq

    On Oct 20, 9:44 pm, myheartinameric a <myheartinamer. ..@gmail.com>
    wrote:
    I was confused about part 2.6 in the comp.lang.c. FAQ.
    >
    2.6 ... <cut>
    Another possibility is to declare the variable-size element very
    large, rather than very small; in the case of the above example:
    >
    ...
    char namestr[MAXSIZE];
    >
    where MAXSIZE is larger than any name which will be stored.
    However, it looks like this technique is disallowed by a strict
    interpretation of the Standard as well. Furthermore, either of
    these "chummy" structures must be used with care, since the
    programmer knows more about their size than the compiler does.
    >
    I thought it was valid to have arrays of characters in a struct. Am I
    missing something?
    You can have arrays in structs.
    The reason the FAQ says this is not valid is because the context is
    close to this

    char (*p)[42] = malloc(30);
    if(p == NULL) return 0;
    strcpy(p[0], "hello world");
    puts(p[0]);

    This will probably compile and "work" as expected by any C
    implementation (including the right headers, main, et cetera).
    However, p[0] is a char[42]; we've only allocated for a char[30].
    Strictly speaking, using p[0] in any way invokes undefined behavior,
    just like using a pointer to any object where not enough memory for
    that object is allocated is UB.

    Comment

    • Keith Thompson

      #3
      Re: question about 2.6 in the faq

      myheartinameric a <myheartinameri ca@gmail.comwri tes:
      I was confused about part 2.6 in the comp.lang.c. FAQ.
      >
      2.6 ... <cut>
      Another possibility is to declare the variable-size element very
      large, rather than very small; in the case of the above example:
      >
      ...
      char namestr[MAXSIZE];
      >
      where MAXSIZE is larger than any name which will be stored.
      However, it looks like this technique is disallowed by a strict
      interpretation of the Standard as well. Furthermore, either of
      these "chummy" structures must be used with care, since the
      programmer knows more about their size than the compiler does.
      >
      >
      I thought it was valid to have arrays of characters in a struct. Am I
      missing
      something? I realize the intent of the FAQ was to explain how not to
      simulate
      variable-length arrays.
      If you'll look at the immediately following code, the idea is to
      allocate *less* than the size of the struct, on the assumption that
      you'll be ok if you don't use array elements beyond the allocated
      size.

      Speaking of variable length, it looks like you're writing long lines
      and then something is wrapping them; the result is alternating long
      and short lines, which can be difficult to read. If you'll keep your
      lines below about 70 columns in the first place, this shouldn't
      happen.

      Compare:
      I thought it was valid to have arrays of characters in a struct. Am I
      missing
      something? I realize the intent of the FAQ was to explain how not to
      simulate
      variable-length arrays.
      versus:
      I thought it was valid to have arrays of characters in a struct. Am
      I missing something? I realize the intent of the FAQ was to explain
      how not to simulate variable-length arrays.
      --
      Keith Thompson (The_Other_Keit h) kst-u@mib.org <http://www.ghoti.net/~kst>
      Nokia
      "We must do something. This is something. Therefore, we must do this."
      -- Antony Jay and Jonathan Lynn, "Yes Minister"

      Comment

      • blargg

        #4
        Re: question about 2.6 in the faq

        In article
        <356fc94a-85f8-4166-95c1-432e66d2decb@s1 g2000prg.google groups.com>,
        vippstar@gmail. com wrote:
        On Oct 20, 9:44 pm, myheartinameric a <myheartinamer. ..@gmail.com>
        wrote:
        I was confused about part 2.6 in the comp.lang.c. FAQ.

        2.6 ... <cut>
        Another possibility is to declare the variable-size element very
        large, rather than very small; in the case of the above example:

        ...
        char namestr[MAXSIZE];

        where MAXSIZE is larger than any name which will be stored.
        However, it looks like this technique is disallowed by a strict
        interpretation of the Standard as well. Furthermore, either of
        these "chummy" structures must be used with care, since the
        programmer knows more about their size than the compiler does.

        I thought it was valid to have arrays of characters in a struct. Am I
        missing something?
        >
        You can have arrays in structs.
        The reason the FAQ says this is not valid is because the context is
        close to this
        >
        char (*p)[42] = malloc(30);
        if(p == NULL) return 0;
        strcpy(p[0], "hello world");
        puts(p[0]);
        >
        This will probably compile and "work" as expected by any C
        implementation (including the right headers, main, et cetera).
        However, p[0] is a char[42]; we've only allocated for a char[30].
        Strictly speaking, using p[0] in any way invokes undefined behavior,
        just like using a pointer to any object where not enough memory for
        that object is allocated is UB.
        Just to give a more obviously UB example that as you hint at, this
        allocates space for only the first int in the struct:

        struct S { int i, j; };

        S* s = malloc( sizeof (int) );
        s->i = 1234; // UB
        printf( "%d\n", s->i ); // UB

        Perhaps one justification a compiler could provide is that S has a
        stricter alignment requirement than int, and since the size passed to
        malloc is less than sizeof (S), it isn't required to align the memory
        sufficiently for S.

        Comment

        • Eric Sosman

          #5
          Re: question about 2.6 in the faq

          blargg wrote:
          >
          Just to give a more obviously UB example that as you hint at, this
          allocates space for only the first int in the struct:
          >
          struct S { int i, j; };
          >
          struct
          S* s = malloc( sizeof (int) );
          s->i = 1234; // UB
          printf( "%d\n", s->i ); // UB
          >
          Perhaps one justification a compiler could provide is that S has a
          stricter alignment requirement than int, and since the size passed to
          malloc is less than sizeof (S), it isn't required to align the memory
          sufficiently for S.
          There was a huge debate about this in c.l.c. about fifteen years
          ago, and the eventual consensus was that all alignment requirements
          must be met even if their possible violation is untestable.

          But there's another problem, not involving alignment. Since the
          compiler "knows" that `s' is either NULL or points to a `struct S'
          object, the compiler is free to generate code that accesses `s->j'
          even if you and I can't see any good reason for it to do so. If
          the memory that ought to hold `s->j' doesn't actually exist, the
          consequences are unpredictable. (Yes, I once debugged just such a
          situation: The details were a little different, but when a "short"
          struct instance happened to fall close to the end of a memory page
          and the compiler decided to pre-fetch a non-existent field several
          lines before the program wanted to use it, all Hell broke loose.)

          --
          Eric.Sosman@sun .com

          Comment

          • blargg

            #6
            Re: question about 2.6 in the faq

            In article <1224538652.646 588@news1nwk>, Eric Sosman <Eric.Sosman@su n.com>
            wrote:
            blargg wrote:

            Just to give a more obviously UB example that as you hint at, this
            allocates space for only the first int in the struct:

            struct S { int i, j; };
            struct
            S* s = malloc( sizeof (int) );
            s->i = 1234; // UB
            printf( "%d\n", s->i ); // UB

            Perhaps one justification a compiler could provide is that S has a
            stricter alignment requirement than int, and since the size passed to
            malloc is less than sizeof (S), it isn't required to align the memory
            sufficiently for S.
            >
            There was a huge debate about this in c.l.c. about fifteen years
            ago, and the eventual consensus was that all alignment requirements
            must be met even if their possible violation is untestable.
            Not sure what you mean "even if their possible violation is untestable".
            Are you saying that malloc(1) must align for example to 8 bytes on
            platforms where double has an 8-byte alignment requirement, even though a
            sizeof(double)> 1 on such a platform? About the only useful consequence of
            that seems to be that you could do

            double* d = malloc(1);
            char* c = (char*) d;
            // use *c...
            free( c ); // well-defined
            But there's another problem, not involving alignment. Since the
            compiler "knows" that `s' is either NULL or points to a `struct S'
            object, the compiler is free to generate code that accesses `s->j'
            even if you and I can't see any good reason for it to do so. If
            the memory that ought to hold `s->j' doesn't actually exist, the
            consequences are unpredictable. (Yes, I once debugged just such a
            situation: The details were a little different, but when a "short"
            struct instance happened to fall close to the end of a memory page
            and the compiler decided to pre-fetch a non-existent field several
            lines before the program wanted to use it, all Hell broke loose.)
            Your example sounds more like this:

            short* p = <logical address that's not yet valid to access>;

            // provide physical backing for address by messing with page tables
            // ...

            // now access memory
            *p = 1234;

            where the compiler is free to move the store to p earlier (and thus cause
            a crash), since p isn't declared to point to a volatile type as it should
            be.

            Comment

            • Eric Sosman

              #7
              Re: question about 2.6 in the faq

              blargg wrote:
              In article <1224538652.646 588@news1nwk>, Eric Sosman <Eric.Sosman@su n.com>
              wrote:
              >
              >blargg wrote:
              >>Just to give a more obviously UB example that as you hint at, this
              >>allocates space for only the first int in the struct:
              >>>
              >> struct S { int i, j; };
              >>>
              > struct
              >> S* s = malloc( sizeof (int) );
              >> s->i = 1234; // UB
              >> printf( "%d\n", s->i ); // UB
              >>>
              >>Perhaps one justification a compiler could provide is that S has a
              >>stricter alignment requirement than int, and since the size passed to
              >>malloc is less than sizeof (S), it isn't required to align the memory
              >>sufficientl y for S.
              > There was a huge debate about this in c.l.c. about fifteen years
              >ago, and the eventual consensus was that all alignment requirements
              >must be met even if their possible violation is untestable.
              >
              Not sure what you mean "even if their possible violation is untestable".
              Are you saying that malloc(1) must align for example to 8 bytes on
              platforms where double has an 8-byte alignment requirement, even though a
              sizeof(double)> 1 on such a platform? About the only useful consequence of
              that seems to be that you could do
              >
              double* d = malloc(1);
              char* c = (char*) d;
              // use *c...
              free( c ); // well-defined
              Yes, that's my report of the consensus. The fragment you
              show has well-defined behavior (assuming "use *c" makes proper
              provision for the case `d == NULL'). Note that "the fragment
              works" is a little weaker than the consensus opinion: All it
              shows is that malloc() returns a pointer that can be converted
              to `double*' without loss of information. My own take on the
              consensus was along the lines of "That which has no observable
              consequences does not exist" -- the philosophy majors are welcome
              to shred me on this one -- but I have no desire to reawaken the
              sterile argument, just to report its existence and its outcome.
              > But there's another problem, not involving alignment. Since the
              >compiler "knows" that `s' is either NULL or points to a `struct S'
              >object, the compiler is free to generate code that accesses `s->j'
              >even if you and I can't see any good reason for it to do so. If
              >the memory that ought to hold `s->j' doesn't actually exist, the
              >consequences are unpredictable. (Yes, I once debugged just such a
              >situation: The details were a little different, but when a "short"
              >struct instance happened to fall close to the end of a memory page
              >and the compiler decided to pre-fetch a non-existent field several
              >lines before the program wanted to use it, all Hell broke loose.)
              >
              Your example sounds more like this:
              >
              short* p = <logical address that's not yet valid to access>;
              >
              // provide physical backing for address by messing with page tables
              // ...
              >
              // now access memory
              *p = 1234;
              >
              where the compiler is free to move the store to p earlier (and thus cause
              a crash), since p isn't declared to point to a volatile type as it should
              be.
              Since C provides no mechanism to produce a "logical address
              that's not yet valid to access," I'm not sure what to make of
              your example. The bug I debugged (with two other people, the
              three of us contributing different skills to the job) did not
              involve the setting of any pointer to an invalid address. Rather,
              the pointer was to a perfectly valid address -- but not to an
              address that was valid for a full-fledged `struct S'.

              --
              Eric Sosman
              esosman@ieee-dot-org.invalid

              Comment

              • myheartinamerica

                #8
                Re: question about 2.6 in the faq

                On Oct 20, 2:08 pm, Keith Thompson <ks...@mib.orgw rote:
                Speaking of variable length, it looks like you're writing long lines
                and then something is wrapping them; the result is alternating long
                and short lines, which can be difficult to read.  If you'll keep your
                lines below about 70 columns in the first place, this shouldn't
                happen.
                >
                Compare:
                >
                I thought it was valid to have arrays of characters in a struct. Am I
                missing
                something? I realize the intent of the FAQ was to explain how not to
                simulate
                variable-length arrays.
                >
                versus:
                >
                I thought it was valid to have arrays of characters in a struct. Am
                I missing something? I realize the intent of the FAQ was to explain
                how not to simulate variable-length arrays.
                I wholeheartedly agree that it was unreadable. I wrote my response in
                Vim wrapped at 80 columns. Then I pasted it into the web interface of
                Google Groups, which doesn't seem to show you where it will wrap.
                Google Groups lets you compose lines that are wrapped by the size of
                the input box. If I resize my browser window, it resizes where the
                lines are wrapped.

                While I appreciate the convenience of Google Groups, I question its
                impact on Usenet readability and quality.

                Anyways, I intend this to be the last message I write from the web
                interface.

                Thanks for your response,
                Mick Beaver

                Comment

                Working...