Inconsistent Program Results

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

    #16
    Re: Inconsistent Program Results

    In article <1173341327.599 135.112420@p10g 2000cwp.googleg roups.com>
    <Francine.Neary @googlemail.com wrote:
    >Surely [using prototypes] makes the code harder to maintain?
    Well, yes; but also no.

    In particular, if you have a prototype declaration for a function,
    and you change the function to take different parameters (but no
    change to its return type), you must find and update the prototype.
    (The parenthetic remark is to distinguish this case from the one
    where you *do* change the return type; in this case, you must
    edit any declaration regardless of whether it is also a prototype.)

    This is "new work" that you did not have to do if you did not use
    a prototype.

    On the obverse, however, if you change a function to take different
    parameters, you must find and update all calls to that function.
    This maintenance *task* is unchanged whether or not you use a
    prototype -- but if you *do* use a prototype, any calls that you
    miss will [1] draw a diagnostic from any correct [2] C compiler.
    This makes the actual maintenance easier: now you can make any
    changes you know you need to make, then start a compilation, and
    make any additional changes the compiler points out.

    There are languages that avoid the need for redundant declarations
    like prototypes. Compilers for these languages (if they are in
    fact compiled) automatically record the actual type of the actual
    definition, and automatically compare calls. In effect, these
    compilers build prototypes for you. C is not such a language, but
    if you use prototypes consistently, you can get most of this benefit,
    at a relatively low cost.

    -----
    [1] Provided the prototype is in scope at the point of the call,
    anyway. Better compilers have flags to warn you if you make a call
    without a prototype in scope, so that you can be sure that all
    calls have a prototype. If you always put such prototypes in a
    "safe" location -- such as a header file that you also include in
    the code that defines the function -- then you get consistent and
    "safe" checking. If you scatter multiple copies of the prototype
    in multiple separate files, however, and/or fail to have an
    appropriate prototype in place at the point of the call, you lose
    the benefit. This is where those Other Languages with "automatic"
    prototyping seem superior, at least up until you have to write the
    compiler for one. :-)

    [2] "Correct" is kind of a weasel-word for "conforming ". A lot of
    modern C compilers will warn even in non-conforming modes, such as
    their default modes, so "conforming " is not quite the right adjective
    anyway.
    --
    In-Real-Life: Chris Torek, Wind River Systems
    Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
    email: forget about it http://web.torek.net/torek/index.html
    Reading email is like searching for food in the garbage, thanks to spammers.

    Comment

    • Keith Thompson

      #17
      Re: Inconsistent Program Results

      Francine.Neary@ googlemail.com writes:
      >The standard headers will contain only declarations;
      >including one of them might trivially slow down compilation, but it
      >won't cause any bloat in your executable.
      >
      OK, that's interesting, thanks.
      I wrote the above (starting with "The standard headers"). Please
      don't delete attribution lines.
      void rdinpt();
      >>
      >Make this:
      >>
      > void rdinput(char **);
      >>
      I don't think it's obligatory to list the parameters yet is it?
      >>
      >It's not obligatory, but you should *always* do it anyway; there's no
      >good reason not to. Using a prototype (i.e., specifying the types of
      >the parameters) allows the compiler to detect certain errors, and in
      >many cases to adjust the argument via an implicit conversion, and
      >costs you nothing.
      >
      Surely it makes the code harder to maintain? If I change the parameter
      types of rdinpt then I'd have to go back and change it consistently
      somewhere else too. As I understand it, the main point of putting in
      this declaration is so that I call rdinpt from a function that occurs
      before its body in the source file, and leaving the parameters
      flexible still achieves this.
      If you change the parameter types, you have to change all the calls as
      well. Leaving the parameter types out doesn't give you any real
      flexibility; it just makes errors more difficult to detect.
      Sounds like more typing :(
      >>
      >More typing, and easier reading. How many times to you type the name?
      >How many times will you and others read it?
      >
      Well, with things like strspn, atoi and setjmp in the standard
      library, brevity of names seems to have good precedents in C.
      Sure, but we know what "atoi" means, and if we don't, we can look it
      up in the standard. We don't have that advantage when we encounter
      the name "rdinpt".

      When the names of the functions in the standard library were chosen,
      some linkers limited external identifiers to just 6 significant
      characters, and hardware (teletypes) was such that long identifiers
      were physically more difficult to type (or so I've heard; it was
      before my time). We're no longer subject to those limitations.
      >Get a better textbook.
      >>
      >main() returns int. Some compilers may allow it to be declared to
      >return void, but there's no advantage in using that. By declaring
      >main to return int, you gain portability, and again, it costs you
      >*nothing*.
      >
      This seems quite a basic point, and I doubt a textbook would get it
      wrong.
      I admit it's surprising, but it happens to be true. "void main()" is
      incorrect, and many textbooks really do get it wrong. See questions
      11.12a through 11.15 in the comp.lang.c FAQ, <http://www.c-faq.com/>.
      And if that doesn't convince you, the latest version of the C standard
      is available at
      <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf>.
      It costs some thought - if you don't have anything useful to return
      from main, what int should you return? 0? 1? 19223?
      0.

      The only portable values you can return from main() are 0,
      EXIT_SUCCESS, and EXIT_FAILURE; the latter are macros defined in
      <stdlib.h>.

      Yes, there are things you have to remember.
      >malloc(), if it succeeds, returns a pointer (of type void*) to a chunk
      >of memory that's guaranteed to be properly aligned for any data type.
      >A void* pointer can be implicitly converted to any pointer-to-object
      >type. The cast is absolutely unnecessary.
      >
      OK then, I'll try to remember not to typecast malloc in future, though
      again it seems very common in all the code I've seen.
      You see a lot of bad code.
      Thanks, I always forget whether to return() or return(0) at the end of
      a void function.
      >>
      >"return();" is a syntax error. "return(0); " or "return 0;" is
      >incorrect in a void function. "return;" is unnecessary unless you're
      >terminating the function early.
      >
      Hah! Now I remember exactly what happened. I started with return(),
      got an error, and assumed I had to return(0) instead - this only gave
      a warning, but gcc is extremely picky and produces lots of warnings,
      so I tend to ignore them. Why is return() a syntax error? That seems
      pretty inconsistent with the syntax for calling a non-built-in
      function with no arguments.
      "return" is *not* a function, it's a keyword. A return statement
      isn't a function call; it's a special kind of statement with its own
      syntax.

      You're cheating yourself by ignoring warning messages. gcc can be
      picky, but most of its warnings are valid. If you have a question
      about a particular warning message, feel free to ask here.

      --
      Keith Thompson (The_Other_Keit h) kst-u@mib.org <http://www.ghoti.net/~kst>
      San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
      "We must do something. This is something. Therefore, we must do this."
      -- Antony Jay and Jonathan Lynn, "Yes Minister"

      Comment

      • jaysome

        #18
        Re: Inconsistent Program Results

        On Thu, 08 Mar 2007 08:29:46 +0000, Richard Heathfield
        <rjh@see.sig.in validwrote:
        >Francine.Neary @googlemail.com said:
        >
        [snip]
        >No - *bad* precedents. But at least everyone knows what those things do.
        >The object and purpose of the rdinpt function is not hanging on the
        >lips of every C student, so they are less likely to know what it means
        >than they are to know what, say, strspn means. Good name choice is an
        >important part of programming, but it seems to be a part that wasn't as
        >well-understood when functions like strspn were being coined.
        And the object and purpose of the readinput function is not
        necessarily hanging on the lips of every C student, especially those
        who play Scrabble and wonder if it means "re-address input" or "read
        in put" or "readin' put". Scrabble players tend to have some wild
        thoughts. It's best to make it unambiguous and name it read_input.

        Regards
        --
        jay

        Comment

        • John Bode

          #19
          Re: Inconsistent Program Results

          On Mar 8, 2:08 am, Francine.Ne...@ googlemail.com wrote:
          The standard headers will contain only declarations;
          including one of them might trivially slow down compilation, but it
          won't cause any bloat in your executable.
          >
          OK, that's interesting, thanks.
          >
          void rdinpt();
          >
          >Make this:
          >
          > void rdinput(char **);
          >
          I don't think it's obligatory to list the parameters yet is it?
          >
          It's not obligatory, but you should *always* do it anyway; there's no
          good reason not to. Using a prototype (i.e., specifying the types of
          the parameters) allows the compiler to detect certain errors, and in
          many cases to adjust the argument via an implicit conversion, and
          costs you nothing.
          >
          Surely it makes the code harder to maintain?
          In the end, maintenance is actually easier because you have more
          robust error checking (maintenance is more than just editing source
          text). You find more errors at compile time, saving you headaches
          later on in the testing phase.
          If I change the parameter
          types of rdinpt then I'd have to go back and change it consistently
          somewhere else too. As I understand it, the main point of putting in
          this declaration is so that I call rdinpt from a function that occurs
          before its body in the source file, and leaving the parameters
          flexible still achieves this.
          In my experience, you make life easier on yourself by defining
          functions before they're used (IOW, you can put the body of the
          rdinput() function before main(); that way you don't need a separate
          declaration). If you need to call a function from multiple source
          files, create a header file that contains a prototype declaration for
          that function, and #include it where necessary. That way you only
          need to make the prototype change in two places (the actual function
          definition, and the header file). Of course you need to change all
          the function calls too.
          >
          Sounds like more typing :(
          >
          More typing, and easier reading. How many times to you type the name?
          How many times will you and others read it?
          >
          Well, with things like strspn, atoi and setjmp in the standard
          library, brevity of names seems to have good precedents in C.
          >
          Remember that C is a product of the early '70s, and brevity was
          necessitated by the limitations of the environment. That's not the
          case anymore, and there's no good reason not to use descriptive names.

          Trust me, function names that read like indigestion noises are not a
          good thing when looking at unfamiliar code (or code that you wrote 6
          months ago and haven't looked at since). I have a C reference manual
          handy for when I don't remember what grplphmp() does; do you provide
          similar documentation?

          The gains of using meaningful, descriptive, plain-old-English names
          far outweigh the minimal pain of typing them. Remember, the
          computer doesn't care whether you call it rdinput or ReadInput, but
          anyone trying to read and understand your code will appreciate the
          latter over the former.
          Get a better textbook.
          >
          main() returns int. Some compilers may allow it to be declared to
          return void, but there's no advantage in using that. By declaring
          main to return int, you gain portability, and again, it costs you
          *nothing*.
          >
          This seems quite a basic point, and I doubt a textbook would get it
          wrong.
          You'd be surprised. The sad fact is that 90% of the books and
          websites on C are *crap*. A distressingly large number of them get
          basic concepts wrong, and many of them teach bad habits. There are
          maybe 5 or 6 references that I consider authoritative.

          Herb Schildt proved that you don't have to know what you're talking
          about to be a successful author of C references.
          >
          It costs some thought - if you don't have anything useful to return
          from main, what int should you return? 0? 1? 19223?
          0 or EXIT_SUCCESS.
          >
          malloc(), if it succeeds, returns a pointer (of type void*) to a chunk
          of memory that's guaranteed to be properly aligned for any data type.
          A void* pointer can be implicitly converted to any pointer-to-object
          type. The cast is absolutely unnecessary.
          >
          OK then, I'll try to remember not to typecast malloc in future, though
          again it seems very common in all the code I've seen.
          >
          <reiterates prior point about 90% of C references being crap>

          Very old implementations of C (pre-ANSI, C89) define malloc() as
          returning char*, and in those implementations the cast *was*
          necessary, since you can't implicitly convert a char* to another
          object pointer type. It could be that the author of your text never
          made it past 1985; however, just because he's living in the past
          doesn't mean you should too.
          Thanks, I always forget whether to return() or return(0) at the end of
          a void function.
          >
          "return();" is a syntax error. "return(0); " or "return 0;" is
          incorrect in a void function. "return;" is unnecessary unless you're
          terminating the function early.
          >
          Hah! Now I remember exactly what happened. I started with return(),
          got an error, and assumed I had to return(0) instead - this only gave
          a warning, but gcc is extremely picky and produces lots of warnings,
          so I tend to ignore them.
          DON'T IGNORE WARNINGS. They're there for a reason. They're telling
          you that you've done something that, while not technically illegal, is
          ill-advised, and may cause problems at runtime.
          Why is return() a syntax error? That seems
          pretty inconsistent with the syntax for calling a non-built-in
          function with no arguments.
          >
          Because return isn't a function; it's a statement. It doesn't have a
          parameter list. It can optionally be followed by an expression, and
          that expression may be surrounded by parens.

          return (0)

          is the same as

          return 0

          because the expressions (0) and 0 evaluate to the same thing.
          However, () is not a valid expression, hence the syntax error.

          If you're getting all of this from your textbook, throw it in the
          trash RIGHT NOW and get something authoritative ("The C Programming
          Language", 2nd ed., Kernighan & Ritchie, is about as authoritative as
          it gets; a good companion is "C: A Reference Manual", 5th ed.,
          Harbison & Steele).

          Comment

          • Dave Vandervies

            #20
            Re: Inconsistent Program Results

            In article <1173360070.950 646.19410@s48g2 000cws.googlegr oups.com>,
            John Bode <john_bode@my-deja.comwrote:
            >On Mar 8, 2:08 am, Francine.Ne...@ googlemail.com wrote:
            malloc(), if it succeeds, returns a pointer (of type void*) to a chunk
            of memory that's guaranteed to be properly aligned for any data type.
            A void* pointer can be implicitly converted to any pointer-to-object
            type. The cast is absolutely unnecessary.
            >>
            >OK then, I'll try to remember not to typecast malloc in future, though
            >again it seems very common in all the code I've seen.
            >>
            >
            ><reiterates prior point about 90% of C references being crap>
            >
            >Very old implementations of C (pre-ANSI, C89) define malloc() as
            >returning char*, and in those implementations the cast *was*
            >necessary, since you can't implicitly convert a char* to another
            >object pointer type. It could be that the author of your text never
            >made it past 1985; however, just because he's living in the past
            >doesn't mean you should too.
            [...]
            >If you're getting all of this from your textbook, throw it in the
            >trash RIGHT NOW and get something authoritative ("The C Programming
            >Language", 2nd ed., Kernighan & Ritchie, is about as authoritative as
            >it gets; a good companion is "C: A Reference Manual", 5th ed.,
            >Harbison & Steele).
            It's worth noting that K&R2 does tell you to cast malloc.

            They have, however, fixed that in the errata (which, for any technical
            book, is the first thing you should go looking for after you buy the
            book).
            They also at least had a good reason - void * was new at the time they
            wrote the book, so they were used to casting the char * that came
            from malloc, and they tested their code with a C++ compiler (which
            also requires the cast) because of a lack of C compilers with the
            new-at-the-time prototype checking and other features.

            Any C book written since, oh, around 1992 that casts malloc is Wrong
            With No Good Reason; the authors should have had a compiler that didn't
            require the cast, though they at least may have had the excuse of old
            habits to fall back on. *Any* book that declares void main is Wrong
            With No Excuse, since it was never necessary or even legal (void was
            introduced in C89, which specifically states that main must return int).


            dave

            --
            Dave Vandervies dj3vande@csclub .uwaterloo.ca
            Two glaring problems and one subtle error is a better-than-average
            outcome in these parts.
            --Eric Sosman in comp.lang.c

            Comment

            • Randy Howard

              #21
              Re: Inconsistent Program Results

              On Thu, 8 Mar 2007 02:08:47 -0500, Francine.Neary@ googlemail.com wrote
              (in article <1173341327.599 135.112420@p10g 2000cwp.googleg roups.com>):
              >Get a better textbook.
              >>
              >main() returns int. Some compilers may allow it to be declared to
              >return void, but there's no advantage in using that. By declaring
              >main to return int, you gain portability, and again, it costs you
              >*nothing*.
              >
              This seems quite a basic point, and I doubt a textbook would get it
              wrong.
              You'd be amazed. There are dozens of books that get it wrong, and
              about half of them are written by Herbert Schildt. If you have a book
              written by him, return it if you can, otherwise use it to start a fire.
              It costs some thought - if you don't have anything useful to return
              from main, what int should you return? 0? 1? 19223?
              The standard being the ultimate textbook on such things, you return 0,
              or EXIT_SUCCESS in such cases.
              >malloc(), if it succeeds, returns a pointer (of type void*) to a chunk
              >of memory that's guaranteed to be properly aligned for any data type.
              >A void* pointer can be implicitly converted to any pointer-to-object
              >type. The cast is absolutely unnecessary.
              >
              OK then, I'll try to remember not to typecast malloc in future, though
              again it seems very common in all the code I've seen.
              Bear in mind that if you are just learning C, you haven't seen much
              code yet, and those replying to you and by and large people that have
              been looking at both good and bad examples of C for years, in some
              cases decades. You were smart enough to find a newsgroup populated by
              a core group of extremely competent C programmers. Be smart enough to
              listen to their advice as well.
              >>Thanks, I always forget whether to return() or return(0) at the end of
              >>a void function.
              >>
              >"return();" is a syntax error. "return(0); " or "return 0;" is
              >incorrect in a void function. "return;" is unnecessary unless you're
              >terminating the function early.
              >
              Hah! Now I remember exactly what happened. I started with return(),
              got an error, and assumed I had to return(0) instead - this only gave
              a warning, but gcc is extremely picky and produces lots of warnings,
              so I tend to ignore them.
              *sigh* Wrong attitude. gcc is extremely helpful, in that it produces
              a lot of warnings that point out problems in your code, which might
              /accidentally/ work on a given day, or a given platform, but are just
              likely to not work today, or tomorrow, or when the boss comes along for
              that all-important demo. Don't ignore them, understand them so that
              you can avoid them in future code.
              Why is return() a syntax error? That seems
              pretty inconsistent with the syntax for calling a non-built-in
              function with no arguments.
              What's wrong with:

              return;

              ???


              Why should the way you call a function have any commonality of syntax
              with the way you return from one?


              --
              Randy Howard (2reply remove FOOBAR)
              "The power of accurate observation is called cynicism by those
              who have not got it." - George Bernard Shaw





              Comment

              Working...