A CT&P excursion - beat it up!

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

    A CT&P excursion - beat it up!

    After completing KNK2, my interest in C was reignited, prompting a
    re-reading of Traister's dubious book on pointers. Then I dusted off
    the fine ``C Traps and Pitfalls'' by Andy Koenig. Nowadays I primarily
    work in PHP, Perl and C++. Consequently my C skills have atrophied.

    So inspired by CT&P's ``calendar'' example on page 30, I decided to
    brush up on some elementary C (although Koenig describes it as walking
    ``out on the ice'') with the little toy exercise below.

    For posting, here I yanked all the comments for brevity. There's a
    (too?) liberal use of typedef, pointers for the sake of pointers and
    some blatantly obvious asserts. It compiles cleanly with Sun C 5.8
    2005/10/13 on a SPARC and lint shows only functions returning values
    that are ignored. The output meets expectations for a non-leap year.

    So look this over being brutal and merciless with your critique as I
    try to rebuild my flabby C muscles:

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

    enum { MONTHS = 12, DAYS = 31 };
    enum { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };
    static const char *const months[] = { "JAN", "FEB", "MAR", "APR",
    "MAY", "JUN", "JUL", "AUG",
    "SEP", "OCT", "NOV", "DEC", };

    int main(void)
    {
    typedef int month_type[DAYS];
    typedef month_type *month_ptr_type ;
    typedef int *day_ptr_type;

    int calendar[MONTHS][DAYS];
    month_ptr_type month_ptr;
    size_t num_months;
    size_t num_days_per_mo nth;

    memset(calendar , 0, sizeof calendar);
    assert(calendar[DEC][DAYS - 1] == 0);

    month_ptr = calendar;
    assert(sizeof *month_ptr == DAYS * sizeof(int));

    num_months = sizeof calendar / sizeof *calendar;
    assert(num_mont hs == MONTHS);

    num_days_per_mo nth = sizeof *calendar / sizeof **calendar;
    assert(num_days _per_month == DAYS);

    for(; month_ptr < calendar + num_months; ++month_ptr) {
    day_ptr_type day_ptr = *month_ptr;
    int month_num = (month_ptr - calendar + num_months) - num_months;
    int days_in_month;
    int day_num;

    switch(month_nu m) {
    case FEB:
    days_in_month = 28;
    break;
    case APR:
    case JUN:
    case SEP:
    case NOV:
    days_in_month = 30;
    break;
    default:
    days_in_month = 31;
    break;
    }

    for(day_num = 1; day_num <= days_in_month; ++day_num, ++day_ptr)
    *day_ptr = day_num;
    }

    for(month_ptr = calendar; month_ptr < calendar + num_months; ++month_ptr) {
    int month_num = (month_ptr - calendar + num_months) - num_months;
    int total_days_in_m onth = -1;
    day_ptr_type day_ptr;

    for(day_ptr = *month_ptr + num_days_per_mo nth - 1;
    day_ptr *month_ptr; --day_ptr) {
    if(*day_ptr) {
    total_days_in_m onth = *day_ptr;
    break;
    }
    }

    printf("month %d (%s) has %d days\n",
    month_num + 1, months[month_num], total_days_in_m onth);
    }

    return EXIT_SUCCESS;
    }

  • Ron Ford

    #2
    Re: A CT&amp;P excursion - beat it up!

    On Sat, 07 Jun 2008 02:08:07 -0500, Bob Nelson wrote:

    [code elided]
    0 warnings, 0 errors.

    On my machine the output doesn't last long enough to read. Add a getchar()
    call somewhere:
    >
    printf("month %d (%s) has %d days\n",
    month_num + 1, months[month_num], total_days_in_m onth);
    getchar();
    }
    >
    return EXIT_SUCCESS;
    }
    I had to write a perpetual calender for my scientific programming class in
    fortran in college. Output was un-fun.
    --
    The chief value of money lies in the fact that one lives in a world in
    which it is overestimated.
    H. L. Mencken

    Comment

    • Richard Heathfield

      #3
      Re: A CT&amp;P excursion - beat it up!

      Ron Ford said:
      On Sat, 07 Jun 2008 02:08:07 -0500, Bob Nelson wrote:
      >
      [code elided]
      0 warnings, 0 errors.
      >
      On my machine the output doesn't last long enough to read. Add a
      getchar() call somewhere:
      Why should he do that? Learn how to drive a command shell.

      --
      Richard Heathfield <http://www.cpax.org.uk >
      Email: -http://www. +rjh@
      Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
      "Usenet is a strange place" - dmr 29 July 1999

      Comment

      • Keith Thompson

        #4
        Re: A CT&amp;P excursion - beat it up!

        Ron Ford <ron@nowhere.ne twrites:
        On Sat, 07 Jun 2008 02:08:07 -0500, Bob Nelson wrote:
        [code elided]
        0 warnings, 0 errors.
        >
        On my machine the output doesn't last long enough to read. Add a getchar()
        call somewhere:
        >
        >>
        > printf("month %d (%s) has %d days\n",
        > month_num + 1, months[month_num], total_days_in_m onth);
        >
        getchar();
        > }
        >>
        > return EXIT_SUCCESS;
        >}
        Don't bother. Just run the program in a way that doesn't cause the
        window to close when the program terminates. Not all systems are
        Windows; on a non-Windows system, a program that gratuitously waits
        for input before terminating would be quite annoying.

        --
        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

        • Ron Ford

          #5
          Re: A CT&amp;P excursion - beat it up!

          On Sat, 07 Jun 2008 08:42:01 +0000, Richard Heathfield wrote:
          Ron Ford said:
          >
          >On Sat, 07 Jun 2008 02:08:07 -0500, Bob Nelson wrote:
          >>
          >[code elided]
          >0 warnings, 0 errors.
          >>
          >On my machine the output doesn't last long enough to read. Add a
          >getchar() call somewhere:
          >
          Why should he do that? Learn how to drive a command shell.
          I use Dev-Cpp for quick and dirty C stuff and simply could not find a
          means, from within the IDE, to pause a program before windows eats it up.
          I ended up putting in a dos-shortcut where the executable lives, and that
          works, but it misses the whole point of staying within an IDE during
          development and testing.:-(

          --
          I confess I enjoy democracy immensely. It is incomparably idiotic, and
          hence incomparably amusing.
          H. L. Mencken

          Comment

          • Flash Gordon

            #6
            Re: A CT&amp;P excursion - beat it up!

            Bob Nelson wrote, On 07/06/08 08:08:

            <snip>
            So look this over being brutal and merciless with your critique as I
            try to rebuild my flabby C muscles:
            >
            #include <assert.h>
            #include <stdio.h>
            #include <stdlib.h>
            #include <string.h>
            >
            enum { MONTHS = 12, DAYS = 31 };
            enum { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };
            static const char *const months[] = { "JAN", "FEB", "MAR", "APR",
            "MAY", "JUN", "JUL", "AUG",
            "SEP", "OCT", "NOV", "DEC", };
            >
            int main(void)
            {
            typedef int month_type[DAYS];
            typedef month_type *month_ptr_type ;
            typedef int *day_ptr_type;
            >
            int calendar[MONTHS][DAYS];
            Rather than the memset further down initialise calendar here
            int calendar[MONTHS][DAYS] = {0};
            month_ptr_type month_ptr;
            size_t num_months;
            size_t num_days_per_mo nth;
            >
            memset(calendar , 0, sizeof calendar);
            assert(calendar[DEC][DAYS - 1] == 0);
            As you say, a someone pointless assert. As are the others below.
            month_ptr = calendar;
            assert(sizeof *month_ptr == DAYS * sizeof(int));
            >
            num_months = sizeof calendar / sizeof *calendar;
            assert(num_mont hs == MONTHS);
            >
            num_days_per_mo nth = sizeof *calendar / sizeof **calendar;
            assert(num_days _per_month == DAYS);
            >
            for(; month_ptr < calendar + num_months; ++month_ptr) {
            Rather than assigning monthptr several lines up I would assign it in the
            for statement.
            for (month_ptr = calendar;
            month_ptr < calendar + num_months;
            ++month_ptr) {

            Well, actually I would not use a pointer at all but instead in index use
            it as an array.
            day_ptr_type day_ptr = *month_ptr;
            int month_num = (month_ptr - calendar + num_months) - num_months;
            <snip>

            This looks horrible complex for a simple problem. Having num_months
            appear twice is somewhat suggestive of a poor job.

            I've not reviewed the rest.
            --
            Flash Gordon

            Comment

            • Richard Heathfield

              #7
              Re: A CT&amp;P excursion - beat it up!

              Ron Ford said:

              <snip>
              I use Dev-Cpp for quick and dirty C stuff and simply could not find a
              means, from within the IDE, to pause a program before windows eats it up.
              If you're running a console-style program, it makes sense to run it from a
              console.
              I ended up putting in a dos-shortcut where the executable lives, and that
              works, but it misses the whole point of staying within an IDE during
              development and testing.:-(
              The whole point being... what, exactly?

              --
              Richard Heathfield <http://www.cpax.org.uk >
              Email: -http://www. +rjh@
              Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
              "Usenet is a strange place" - dmr 29 July 1999

              Comment

              • Ben Bacarisse

                #8
                Re: A CT&amp;P excursion - beat it up!

                Bob Nelson <bnelson@nelson be.comwrites:
                After completing KNK2, my interest in C was reignited, prompting a
                re-reading of Traister's dubious book on pointers. Then I dusted off
                the fine ``C Traps and Pitfalls'' by Andy Koenig. Nowadays I primarily
                work in PHP, Perl and C++. Consequently my C skills have atrophied.
                >
                So inspired by CT&P's ``calendar'' example on page 30, I decided to
                brush up on some elementary C (although Koenig describes it as walking
                ``out on the ice'') with the little toy exercise below.
                I don't know what KNK2 nor CT&P are, but I don't think that matters...

                <snip>
                So look this over being brutal and merciless with your critique as I
                try to rebuild my flabby C muscles:
                >
                #include <assert.h>
                #include <stdio.h>
                #include <stdlib.h>
                #include <string.h>
                >
                enum { MONTHS = 12, DAYS = 31 };
                enum { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };
                static const char *const months[] = { "JAN", "FEB", "MAR", "APR",
                "MAY", "JUN", "JUL", "AUG",
                "SEP", "OCT", "NOV", "DEC", };
                >
                int main(void)
                {
                typedef int month_type[DAYS];
                Some people object to typedefs (external declarations) inside
                functions. I don't, but you may find people who do.

                Array types are odd and although you use them correctly, some people
                will consider it obfuscation to have them at all.
                typedef month_type *month_ptr_type ;
                Pointers to array types are even rarer. I don't think it makes
                anything clearer in this program.
                typedef int *day_ptr_type;
                And finally, there is a school of thought that says the pointerlyness
                of type (that it is a pointer) should not be hidden inside a typedef
                unless you are making an opaque handle type. You are not, and I think
                you've obscured things a little with all these typedefs. Note how you
                feel you want to name the type with _ptr_ so you don't get lost. Much
                better to let the * in the declaration do that for you.

                For example, if you do need to have the pointed-to type named, try
                this:

                typedef int day_type;

                and simply declare your variables:

                day_type *dp;

                but even that is overkill here, I feel.
                int calendar[MONTHS][DAYS];
                Having done all of the above, why not

                month_type calendar[MONTHS];

                If you've gone the effort of having an array type, why not use it?
                month_ptr_type month_ptr;
                size_t num_months;
                size_t num_days_per_mo nth;
                >
                memset(calendar , 0, sizeof calendar);
                assert(calendar[DEC][DAYS - 1] == 0);
                >
                month_ptr = calendar;
                assert(sizeof *month_ptr == DAYS * sizeof(int));
                >
                num_months = sizeof calendar / sizeof *calendar;
                assert(num_mont hs == MONTHS);
                >
                num_days_per_mo nth = sizeof *calendar / sizeof **calendar;
                assert(num_days _per_month == DAYS);
                >
                for(; month_ptr < calendar + num_months; ++month_ptr) {
                day_ptr_type day_ptr = *month_ptr;
                int month_num = (month_ptr - calendar + num_months) - num_months;
                Why not: 'int month_num = month_ptr - calendar;'?
                int days_in_month;
                int day_num;
                >
                switch(month_nu m) {
                case FEB:
                days_in_month = 28;
                break;
                case APR:
                case JUN:
                case SEP:
                case NOV:
                days_in_month = 30;
                break;
                default:
                days_in_month = 31;
                break;
                }
                I'd do this with an array rather than a switch.
                for(day_num = 1; day_num <= days_in_month; ++day_num, ++day_ptr)
                *day_ptr = day_num;
                Slightly more idiomatic might be:

                for(day_num = 0; day_num < days_in_month; ++day_num)
                *day_ptr++ = day_num + 1;
                }
                >
                for(month_ptr = calendar; month_ptr < calendar + num_months; ++month_ptr) {
                int month_num = (month_ptr - calendar + num_months) - num_months;
                see above.
                int total_days_in_m onth = -1;
                day_ptr_type day_ptr;
                >
                for(day_ptr = *month_ptr + num_days_per_mo nth - 1;
                day_ptr *month_ptr; --day_ptr) {
                if(*day_ptr) {
                total_days_in_m onth = *day_ptr;
                break;
                }
                }
                >
                printf("month %d (%s) has %d days\n",
                month_num + 1, months[month_num], total_days_in_m onth);
                }
                >
                return EXIT_SUCCESS;
                }
                Overall I am a little baffled. What is all the array setting for? Is
                this part of a larger program that will do more? If I wanted the
                output that you get, I'd just write:

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

                enum { MONTHS = 12, DAYS = 31 };
                enum { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };

                static const char *const months[] = {
                "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
                "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
                };

                static const int month_len[] = {
                31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
                };

                int main(void)
                {
                int month_num;
                for (month_num = 0; month_num < MONTHS; month_num++)
                printf("month %d (%s) has %d days\n",
                month_num + 1, months[month_num], month_len[month_num]);
                return EXIT_SUCCESS;
                }

                but I am sure that misses the point of your code.

                --
                Ben.

                Comment

                • Bartc

                  #9
                  Re: A CT&amp;P excursion - beat it up!


                  "Richard Heathfield" <rjh@see.sig.in validwrote in message
                  news:QdmdnVFlnp W679fVnZ2dnUVZ8 tHinZ2d@bt.com. ..
                  Ron Ford said:
                  >
                  <snip>
                  >
                  >I use Dev-Cpp for quick and dirty C stuff and simply could not find a
                  >means, from within the IDE, to pause a program before windows eats it up.
                  >
                  If you're running a console-style program, it makes sense to run it from a
                  console.
                  Yes, once it's finished. At that point you don't want the pause anymore.
                  >
                  >I ended up putting in a dos-shortcut where the executable lives, and that
                  >works, but it misses the whole point of staying within an IDE during
                  >development and testing.:-(
                  >
                  The whole point being... what, exactly?
                  The point being that it's supposed to be Integrated.

                  -- bartc


                  Comment

                  • Richard Heathfield

                    #10
                    Re: A CT&amp;P excursion - beat it up!

                    Bartc said:
                    >
                    "Richard Heathfield" <rjh@see.sig.in validwrote in message
                    news:QdmdnVFlnp W679fVnZ2dnUVZ8 tHinZ2d@bt.com. ..
                    >Ron Ford said:
                    >>
                    <snip>
                    >>
                    >>I ended up putting in a dos-shortcut where the executable lives, and
                    >>that works, but it misses the whole point of staying within an IDE
                    >>during development and testing.:-(
                    >>
                    >The whole point being... what, exactly?
                    >
                    The point being that it's supposed to be Integrated.
                    I guess we all have different ways of working. My way involves a lot of
                    xterm sessions. They all appear on the same screen, so I guess you could
                    call them integrated. No getchar call required, though.

                    --
                    Richard Heathfield <http://www.cpax.org.uk >
                    Email: -http://www. +rjh@
                    Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
                    "Usenet is a strange place" - dmr 29 July 1999

                    Comment

                    • gw7rib@aol.com

                      #11
                      Re: A CT&amp;P excursion - beat it up!

                      On 7 Jun, 08:08, Bob Nelson <bnel...@nelson be.comwrote:
                      So look this over being brutal and merciless with your critique as I
                      try to rebuild my flabby C muscles:
                      I haven't read the whole thing, but this line caught my eye:
                      enum { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };
                      This will make JAN equal to zero, FEB one, etc. To my mind this is a
                      problem in the making. Someone somewhere will decide not to use your
                      enums and will plug in a month number directly - for example, 4 for
                      April. Which your program will instead take as being May. So the
                      program will give strange results, and the other person won't know
                      why.

                      Hope that is of some help.
                      Paul.

                      Comment

                      • Flash Gordon

                        #12
                        Re: A CT&amp;P excursion - beat it up!

                        gw7rib@aol.com wrote, On 07/06/08 17:42:
                        On 7 Jun, 08:08, Bob Nelson <bnel...@nelson be.comwrote:
                        >So look this over being brutal and merciless with your critique as I
                        >try to rebuild my flabby C muscles:
                        >
                        I haven't read the whole thing, but this line caught my eye:
                        >
                        >enum { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };
                        >
                        This will make JAN equal to zero, FEB one, etc. To my mind this is a
                        problem in the making. Someone somewhere will decide not to use your
                        enums and will plug in a month number directly - for example, 4 for
                        April. Which your program will instead take as being May. So the
                        program will give strange results, and the other person won't know
                        why.
                        A C programmer of any real experience who assumes that something is
                        numbered from 1 is a fool because in C numbering is commonly done from 0.
                        --
                        Flash Gordon

                        Comment

                        • Bartc

                          #13
                          Re: A CT&amp;P excursion - beat it up!


                          "Flash Gordon" <spam@flash-gordon.me.ukwro te in message
                          news:b0vqh5xkot .ln2@news.flash-gordon.me.uk...
                          gw7rib@aol.com wrote, On 07/06/08 17:42:
                          >On 7 Jun, 08:08, Bob Nelson <bnel...@nelson be.comwrote:
                          >>So look this over being brutal and merciless with your critique as I
                          >>try to rebuild my flabby C muscles:
                          >>
                          >I haven't read the whole thing, but this line caught my eye:
                          >>
                          >>enum { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };
                          >>
                          >This will make JAN equal to zero, FEB one, etc. To my mind this is a
                          >problem in the making. Someone somewhere will decide not to use your
                          >enums and will plug in a month number directly - for example, 4 for
                          >April. Which your program will instead take as being May. So the
                          >program will give strange results, and the other person won't know
                          >why.
                          >
                          A C programmer of any real experience who assumes that something is
                          numbered from 1 is a fool because in C numbering is commonly done from 0.
                          If certain sequences already have a natural or well-known numbering, like
                          the months of the year, then it makes sense to use that same ordering.

                          See K&R2, p111, where they've done just that.

                          --
                          Bartc


                          Comment

                          • soscpd

                            #14
                            Re: A CT&amp;P excursion - beat it up!


                            Hello Ron, List

                            Try that:

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

                            int main(int argc, char *argv[])
                            {

                            system("PAUSE") ;
                            return 0;
                            }

                            Regards
                            Rafael

                            Comment

                            • Richard Tobin

                              #15
                              Re: A CT&amp;P excursion - beat it up!

                              In article <ec795853-8152-4ce2-891a-73e946ec992f@k3 0g2000hse.googl egroups.com>,
                              <gw7rib@aol.com wrote:
                              >enum { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };
                              >This will make JAN equal to zero, FEB one, etc. To my mind this is a
                              >problem in the making. Someone somewhere will decide not to use your
                              >enums and will plug in a month number directly - for example, 4 for
                              >April.
                              They're going to be awfully disappointed with the standard struct tm
                              then, because its tm_mon has 0 for January.

                              -- Richard

                              --
                              In the selection of the two characters immediately succeeding the numeral 9,
                              consideration shall be given to their replacement by the graphics 10 and 11 to
                              facilitate the adoption of the code in the sterling monetary area. (X3.4-1963)

                              Comment

                              Working...