Thread: Seeking advisor for 'C' programming examples -> YouTube

  1. #1
    Registered User
    Join Date
    Jul 2012
    Location
    Michigan U.P.
    Posts
    20

    Seeking advisor for 'C' programming examples -> YouTube

    In a distant past, I did 'C' programming under many flavors of UNIX, and MS-DOS (during the Windows 3.x days). Not as a hobby, as a career.

    I am preparing 'C' programming "learning examples" with intended target being the Linux / Raspberry Pi maker community. These will be formed into YouTube videos, with all source uploaded to GitHub.

    Since these are meant to be educational, I'm concerned that I don't want to convey inaccurate code examples, even if they compile and run OK.

    If the individual reading this would volunteer their time to review my programs as an 'advisor', I would have much greater confidence in what I present to YouTube.

    I cannot pay for your effort, but I will certainly promote you in all code segments and in the YouTube videos, if you find that attractive.

    Appreciated!

    PS: I make no money from this project, neither will I monetize the YouTube content. I'm doing this to give back to the 'Net, and because I like 'C'.

    I have attached one example program for review.
    Attached Files Attached Files

  2. #2
    Programming Wraith GReaper's Avatar
    Join Date
    Apr 2009
    Location
    Greece
    Posts
    2,738
    I just have a question: Why do you prototype the main function before you define it? I see no point in that.
    Devoted my life to programming...

  3. #3
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    Comments as //!!
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    //!! these should be declared as const pointers
    //!! also, adding names does no harm either as it helps to document the function
    //!! void reverse ( const char *start, const char *end );
    void reverse(char *, char *);         /* here is where the recursion happens */
    
    //!! there is no need to prototype main - you shouldn't even try.
    int  main(void);
    
    int main()           /* examine pointer math, recursion */
    {
    //!! the indentation should be consistent
    //!! the pointers should be const char *
    char *srcstr;
    char *startp;
    char *endp;
    int  slen;
    
        //!! this is non-portable.
        system("clear");
        printf("\nProgram begin.\n\n");
    
        //!! beware of meaningless comments - why is 78 significant here?
        srcstr = "abcdefghijklmnopqrstuvwxyz0123456789 The hot brown fox swam in the cool lake.";     /* 78 characters */
        startp = srcstr;
    
        //!! perhaps it would have been better to declare slen as a size_t instead.
        slen = (int) strlen(srcstr);
        //!! NULL (4 letters upper case is used when referring to the pointer that points nowhere.
        //!! nul (3 letters lower case) refers to the character with value '\0'.
        endp = startp + (slen - 1);           /* without the "slen - 1", endp would point to the NULL character terminating the string */
    
        printf("Character string (length %d):  %s\n\n", slen, srcstr);
    
        //!! to print with %p, you should really cast the pointer to void*
        //!! eg. printf("%p",(void*)startp);
        //!! also, talking about 'hex addresses' supposes that %p prints in hex.
        //!! The standard simply states that %p renders a void* pointer as 
        //!! "a sequence of printing characters, in an implementation-defined manner."
        printf("\"startp\" contains memory-space address %p, which points to '%c'\n", startp, *startp);   /* doing subtraction on the hex */
        printf("\"endp\"   contains memory-space address %p, which points to '%c'\n\n", endp, *endp);     /* addresses gives us 77, which */
                                                                                                          /* is explained by how we count */
        printf("Character found at \"startp + 5\": %c\n", *(startp + 5));                                 /* from start-point of the string */
        printf("Character found at \"startp + 26\": %c\n\n", *(startp + 26));
    
        printf("Reversed string:\n\n");
        reverse(startp, endp);
        //!! to me, this seems like a hacky bug caused by poor code.
        printf("%c\n\n", *startp);  /* our silly reverse() function fails to print endp when endp = startp    */
                                    /* so we have to accommodate this quirk and print startp for completeness */
        printf("Done.\n\n");
        return(0);
    }
    
    /* description:   we first enter this function with startp pointing to the beginning of the string */
    /*                and endp pointing to the end of the string.  As both pointers are clearly NOT    */
    /*                pointing at the same memory-space location, we print the CONTENT of endp (which  */
    /*                is the last character of the string).  Then we decrement the pointer.            */
    /*                Nothing says we can't - at this point IN reverse() - call the reverse() function */
    /*                which starts the recursion.  This cycling continues, decrementing endp, until    */
    /*                endp = startp, at which point THE RECURSION STOPS and the "layers of function    */
    /*                calls" effectively evaporate.                                                    */
    
    /* Imagine...  You walk outside your house, and see 13 pieces of trash in your yard. */
    /*             Your job is to throw away each piece of trash.  So, you get the first */
    /*             piece of trash, and throw it away.  Now, INSTEAD of walking over to   */
    /*             the next piece of trash, you CLONE yourself, and wait for your CLONE  */
    /*             to handle the next piece of trash.  So your CLONE gets the next trash */
    /*             and throws it away, then waits.  He creates another CLONE, who gets   */
    /*             the next bit of trash to throw away.  This continues until there are  */
    /*             12 CLONES of you (plus you) standing in the trash-free yard, at which */
    /*             point all your CLONES disappear.  It could be said you alone did all  */
    /*             the work, but in an unusual manner.  In the real world, you would do  */
    /*             the cleanup by sequentially getting and disposing each trash item.    */
    
    //!! I don't think the idea of CLONES is really that helpful.  There is no CLONEing going
    //!! on when one function calls another function, any more than when one function calls itself.
    //!! There is simply a memory of where you were, so you can get back to where you left off
    //!! when you're done.
    //!! The trash yard example can be simply stated as:
    //!! if there is a piece of trash in front of you, pick that up first, otherwise pick up
    //!! the piece of trash behind you.
    
    
    void reverse(char *startp, char *endp)
    {
        if (startp != endp)
            {
            printf("%c", *endp);
            endp--;
            reverse(startp, endp);
            }
    }
    Here's my take on reverse.
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void reverse2(const char *s, const char *e) {
      if ( e > s ) {
        printf("%c",*--e);
        reverse2(s,e);
      }
    }
    
    int main()
    {
        const char *a = "hello";
        printf("reverse of >>%s<< is >>",a);
        reverse2(a,a+strlen(a));
        printf("<<\n");
        const char *b = "";
        printf("reverse of >>%s<< is >>",b);
        reverse2(b,b+strlen(b));
        printf("<<\n");
        const char *c = "this is magic";
        printf("reverse of word in >>%s<< is >>",c);
        reverse2(c+5,c+7);
        printf("<<\n");
        return(0);
    }
    There's no messy -1 when working out the end of the string, and no even messier having to print the first characters of the string separately.

    Recursion can be good when you have self-similar problems.
    Code:
    int factorial ( int n ) {
      if ( n == 1 ) return 1;
      else return n * factorial(n-1);
    }
    int strlen ( const char *p ) {
      if ( *p == '\0' ) return 0;
      else return 1 + strlen(p+1);
    }


    Also, any recursive function can also be expressed as a non-recursive function containing only a loop (and possibly a queue).
    Code:
    int factorial ( int n ) {
      int result = 1;
      for ( i = 2 ; i <= n ; i++ ) {
        result *= i;
      }
      return result;
    }
    int strlen ( const char *p ) {
      int len = 0;
      while ( *p++ != '\0' ) len++;
      return len;
    }
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  4. #4
    Registered User
    Join Date
    Jul 2012
    Location
    Michigan U.P.
    Posts
    20
    Quote Originally Posted by GReaper View Post
    I just have a question: Why do you prototype the main function before you define it? I see no point in that.
    Certainly - you see no point. With respect, I asked for an advisor, not individual-take critiques. Though... I respect that you and a zillion other 'C' programmers choose other code styles, formats, techniques.

    Maybe I posted this request with an excess of innocence. These coders like "// " comments. Those coders like different indenting.

    If something I've written in comments is wrong, misleading or could be reworded... that's what I'm keen to change so those new to 'C' are not "taught wrong". But I can't accommodate dozens of different "I'd do it this way" or "why do you do it that way".

    If the WAY I've coded is wrong, one would think I'd get compiler errors. In absence of errors, a working program is one person's way of accomplishing the intent.

  5. #5
    Registered User
    Join Date
    Jul 2012
    Location
    Michigan U.P.
    Posts
    20
    Quote Originally Posted by Salem View Post
    Comments as //!!
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    //!! these should be declared as const pointers
    //!! also, adding names does no harm either as it helps to document the function
    //!! void reverse ( const char *start, const char *end );
    void reverse(char *, char *);         /* here is where the recursion happens */
    
    //!! there is no need to prototype main - you shouldn't even try.
    int  main(void);
    
    int main()           /* examine pointer math, recursion */
    {
    //!! the indentation should be consistent
    //!! the pointers should be const char *
    char *srcstr;
    char *startp;
    char *endp;
    int  slen;
    
        //!! this is non-portable.
        system("clear");
        printf("\nProgram begin.\n\n");
    
        //!! beware of meaningless comments - why is 78 significant here?
        srcstr = "abcdefghijklmnopqrstuvwxyz0123456789 The hot brown fox swam in the cool lake.";     /* 78 characters */
        startp = srcstr;
    
        //!! perhaps it would have been better to declare slen as a size_t instead.
        slen = (int) strlen(srcstr);
        //!! NULL (4 letters upper case is used when referring to the pointer that points nowhere.
        //!! nul (3 letters lower case) refers to the character with value '\0'.
        endp = startp + (slen - 1);           /* without the "slen - 1",  endp would point to the NULL character terminating the string */
    
        printf("Character string (length %d):  %s\n\n", slen, srcstr);
    
        //!! to print with %p, you should really cast the pointer to void*
        //!! eg. printf("%p",(void*)startp);
        //!! also, talking about 'hex addresses' supposes that %p prints in hex.
        //!! The standard simply states that %p renders a void* pointer as 
        //!! "a sequence of printing characters, in an implementation-defined manner."
        printf("\"startp\" contains memory-space address %p, which points to  '%c'\n", startp, *startp);   /* doing subtraction on the hex */
        printf("\"endp\"   contains memory-space address %p, which points to  '%c'\n\n", endp, *endp);     /* addresses gives us 77, which */
                                                                                                           /* is explained by how we count */
        printf("Character found at \"startp + 5\": %c\n", *(startp + 5));                                  /* from start-point of the string */
        printf("Character found at \"startp + 26\": %c\n\n", *(startp + 26));
    
        printf("Reversed string:\n\n");
        reverse(startp, endp);
        //!! to me, this seems like a hacky bug caused by poor code.
        printf("%c\n\n", *startp);  /* our silly reverse() function fails to print endp when endp = startp    */
                                    /* so we have to accommodate this quirk and print startp for completeness */
        printf("Done.\n\n");
        return(0);
    }
    
    /* description:   we first enter this function with startp pointing to the beginning of the string */
    /*                and endp pointing to the end of the string.  As both pointers are clearly NOT    */
    /*                pointing at the same memory-space location, we print the CONTENT of endp (which  */
    /*                is the last character of the string).  Then we decrement the pointer.            */
    /*                Nothing says we can't - at this point IN reverse() - call the reverse() function */
    /*                which starts the recursion.  This cycling continues, decrementing endp, until    */
    /*                endp = startp, at which point THE RECURSION STOPS and the "layers of function    */
    /*                calls" effectively evaporate.                                                    */
    
    /* Imagine...  You walk outside your house, and see 13 pieces of trash in your yard. */
    /*             Your job is to throw away each piece of trash.  So, you get the first */
    /*             piece of trash, and throw it away.  Now, INSTEAD of walking over to   */
    /*             the next piece of trash, you CLONE yourself, and wait for your CLONE  */
    /*             to handle the next piece of trash.  So your CLONE gets the next trash */
    /*             and throws it away, then waits.  He creates another CLONE, who gets   */
    /*             the next bit of trash to throw away.  This continues until there are  */
    /*             12 CLONES of you (plus you) standing in the trash-free yard, at which */
    /*             point all your CLONES disappear.  It could be said you alone did all  */
    /*             the work, but in an unusual manner.  In the real world, you would do  */
    /*             the cleanup by sequentially getting and disposing each trash item.    */
    
    //!! I don't think the idea of CLONES is really that helpful.  There is no CLONEing going
    //!! on when one function calls another function, any more than when one function calls itself.
    //!! There is simply a memory of where you were, so you can get back to where you left off
    //!! when you're done.
    //!! The trash yard example can be simply stated as:
    //!! if there is a piece of trash in front of you, pick that up first, otherwise pick up
    //!! the piece of trash behind you.
    
    
    void reverse(char *startp, char *endp)
    {
        if (startp != endp)
            {
            printf("%c", *endp);
            endp--;
            reverse(startp, endp);
            }
    }
    Here's my take on reverse.
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void reverse2(const char *s, const char *e) {
      if ( e > s ) {
        printf("%c",*--e);
        reverse2(s,e);
      }
    }
    
    int main()
    {
        const char *a = "hello";
        printf("reverse of >>%s<< is >>",a);
        reverse2(a,a+strlen(a));
        printf("<<\n");
        const char *b = "";
        printf("reverse of >>%s<< is >>",b);
        reverse2(b,b+strlen(b));
        printf("<<\n");
        const char *c = "this is magic";
        printf("reverse of word in >>%s<< is >>",c);
        reverse2(c+5,c+7);
        printf("<<\n");
        return(0);
    }
    There's no messy -1 when working out the end of the string, and no even messier having to print the first characters of the string separately.

    Recursion can be good when you have self-similar problems.
    Code:
    int factorial ( int n ) {
      if ( n == 1 ) return 1;
      else return n * factorial(n-1);
    }
    int strlen ( const char *p ) {
      if ( *p == '\0' ) return 0;
      else return 1 + strlen(p+1);
    }


    Also, any recursive function can also be expressed as a non-recursive function containing only a loop (and possibly a queue).
    Code:
    int factorial ( int n ) {
      int result = 1;
      for ( i = 2 ; i <= n ; i++ ) {
        result *= i;
      }
      return result;
    }
    int strlen ( const char *p ) {
      int len = 0;
      while ( *p++ != '\0' ) len++;
      return len;
    }
    Since you gave your time to elaborate, I'll respond in detail.

    1) thanks for suggesting "const pointers". I've never used such. I'll have to study that.
    2) prototyping main() doesn't cause an error - at least using the most vanilla "gcc file.c -o file" approach. It's partially habit, and I feel it adds clarity.
    3) I try to make my indentation as consistent as I can - in the manner of indenting I use.
    4) I commented "78 characters" just to save the viewer from counting, to keep in their mind when viewing actual program output
    5) yes, system("clear"); is non-portable. I should have added a comment to that effect
    6) casting strlen() to int was my effort to not jump straight into the ocean of 'strange' variable types. The beginner has to learn 'int'. they will eventually understand 'size_t"
    7) NULL vs. nul -- that's something I did not know the fine distinction between. I'll have to study that.
    8) using (void *) when associated with a printf() of %p -- if I can think as a beginner -- seems a strange addition. Looks like I'll have to re-study %p / printing out 'hex'
    9) I don't know what "hacky bug" means, and labelling it "bad code" does little to specify what is bad
    10) "clones of a person cleaning up a yard" was the best I could come up with. In your opinion, you present a different manner of explanation. You were busy when I came up with the idea.

    I do appreciate your comments that propose more accurate code. Since this compiles fine and seems to work as intended, I'm not sure how it can be 'wrong'.
    I've tried to balance rank-beginner code with modest sophistication, yet try to keep things simple. Your comments that I'll need to study & learn more about are worthwhile.

    And I respect your effort in this one case... and your apparent choice to leave it at that. I am asking for somebody to give of their time, which today is very valuable.

    Thanks.

  6. #6
    Registered User
    Join Date
    Jul 2012
    Location
    Michigan U.P.
    Posts
    20
    To specify, I'm seeking an advisor. Posting code was meant to introduce my style, content.

    A thousand comments to "use the // commenting method" are all opinions. I'll listen to such
    opinions, and very well may ignore them all, because my compilations succeed and the
    code executes OK.

    Reply from 'Salem' had constructive comments I will need to study, so I acknowledge that.

    Could be no 'advisor' will seek further connection with my project. And that is totally fine.

    Shall we avoid further dissection of my recursion code? Best.

  7. #7
    Programming Wraith GReaper's Avatar
    Join Date
    Apr 2009
    Location
    Greece
    Posts
    2,738
    Programmers don't write unnecessary code, and your main prototype is just that. Any excuse about habit, supposed clarity or "it compiles just fine" is irrelevant.

    Think of it this way. Your viewers may get the idea that it's mandatory to declare all functions before you define them, which is just false. Instead of getting all defensive about it, I suggest you explicitly mention in your video that that line isn't needed at all and is just put there due to personal preference.
    Devoted my life to programming...

  8. #8
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    There is no point whatsoever in prototyping main. Nobody does it. In twenty years of using C I've never seen it.

    Your code is total crap and your "comments" are even worse. You are simply not good enough to do this, and your understanding of recursion is virtually nil.

    As you have no apparent skill at C programming, please do the internet a favor and refrain from adding to the piles of beginner video garbage that already exist.

  9. #9
    Registered User rstanley's Avatar
    Join Date
    Jun 2014
    Location
    New York, NY
    Posts
    1,110
    I have yet to see an online video that teaches the C Programming Language, effectively. If you are unwilling to teach C using established C Standards such as C99, C11, or at the very least, C89/90, then unfortunately, I can't see yours to be effective either.

    I'm concerned that I don't want to convey inaccurate code examples ...
    Yet, you prototype main(). You only "define" main()! You DO NOT declare/prototype or call main()!!! @Salem has pointed out many other points of concern!

    casting strlen() to int was my effort to not jump straight into the ocean of 'strange' variable types. The beginner has to learn 'int'. they will eventually understand 'size_t"
    There is nothing "Strange" about well established types defined in the C Standards! If you don't know that signed int and an unsigned size_t type may even be different sizes, then you need to go back to a good book on C, before attempting to teach someone C! Each type should be used where it's appropriate, or in the case of the strlen() function, or the sizeof keyword, where required by the return type (strlen) or type (sizeof).

    Since this compiles fine and seems to work as intended, I'm not sure how it can be 'wrong'.
    Just because something compiles cleanly and appears to run without apparent errors, doesn't mean it's wrong or that there aren't any bugs! We don't know:

    What O/S you are compiling on?

    What compiler, and version are you using?

    What C Standard, if any does your compiler use?

    Compilers, and O/S / Compiler combinations are different. People watching your videos will probably use different O/S's and compilers. The code you demonstrate online, MUST work on all these systems!

    thanks for suggesting "const pointers". I've never used such. I'll have to study that.
    Sorry, but this indicates that you need to go back and re-study C from the ground up, paying close attention to the C99 and C11 Standards!

    I am sorry to discourage you, but I am a stickler for teaching C, (Or any subject for that matter) accurately!

  10. #10
    SnoopFrogg KingFlippyNips's Avatar
    Join Date
    Sep 2016
    Posts
    33
    What's the name of your YouTube channel? I normally browse this forum, Reddit, and YouTube videos to help me become a better programmer.

  11. #11
    Registered User
    Join Date
    Nov 2012
    Posts
    1,393
    When teaching, try to keep the examples simple. For example, show how to print a string forward and reverse, and you can already talk about a lot of things, like functions, strings, string length, '\n' etc, sizeof, indexing, pointers, command-line arguments (make srcstr a parameter), etc.

    Also don't put explanations in code comments. Leave the code as clean as possible, and write the explanation elsewhere:

    Code:
    #include <stdio.h>
    
    void print(char const s[], int slen);
    void preverse(char const s[], int slen);
    
    int main()
    {
        char const srcstr[] = "abcdefghijklmnopqrstuvwxyz0123456789 The hot brown fox swam in the cool lake.";
        int const srcstr_len = sizeof(srcstr) - 1;
    
        print(srcstr, srcstr_len);
        preverse(srcstr, srcstr_len);
    }
    
    void print(char const s[], int slen)
    {
        for (int i = 0; i < slen; i++)
            putchar(s[i]);
        putchar('\n');
    }
    
    void preverse(char const s[], int slen)
    {
        for (int i = slen-1; i >= 0; i--)
            putchar(s[i]);
        putchar('\n');
    }

  12. #12
    Registered User
    Join Date
    Jul 2012
    Location
    Michigan U.P.
    Posts
    20
    Quote Originally Posted by KingFlippyNips View Post
    What's the name of your YouTube channel? I normally browse this forum, Reddit, and YouTube videos to help me become a better programmer.
    KFN - it's probably best you better your skills with the guidance of the experts here.

  13. #13
    Registered User
    Join Date
    Jul 2012
    Location
    Michigan U.P.
    Posts
    20
    Quote Originally Posted by c99tutorial View Post
    When teaching, try to keep the examples simple. For example, show how to print a string forward and reverse, and you can already talk about a lot of things, like functions, strings, string length, '\n' etc, sizeof, indexing, pointers, command-line arguments (make srcstr a parameter), etc.

    Also don't put explanations in code comments. Leave the code as clean as possible, and write the explanation elsewhere:

    Code:
    #include <stdio.h>
    
    void print(char const s[], int slen);
    void preverse(char const s[], int slen);
    
    int main()
    {
        char const srcstr[] = "abcdefghijklmnopqrstuvwxyz0123456789 The hot brown fox swam in the cool lake.";
        int const srcstr_len = sizeof(srcstr) - 1;
    
        print(srcstr, srcstr_len);
        preverse(srcstr, srcstr_len);
    }
    
    void print(char const s[], int slen)
    {
        for (int i = 0; i < slen; i++)
            putchar(s[i]);
        putchar('\n');
    }
    
    void preverse(char const s[], int slen)
    {
        for (int i = slen-1; i >= 0; i--)
            putchar(s[i]);
        putchar('\n');
    }
    Thanks much for the supportive, tactful comment. Aside - what's your take on running code through some variety of 'lint'?
    I'm on Ubuntu 16.04 w/ gcc 5.4.0

  14. #14
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Lint can be pretty aggressive about certain valid constructs, like an if without an else, or a switch without a default clause even when I know it doesn't make a difference. I personally prefer the compiler's analysis, because it generating a message means it's definitely a problem. A lot of the analysis a lint program would do can also be done by a compiler with warnings turned on. So as long as you compile your examples carefully (i.e. use -Wall and other warning switches) you will get lint-like output. Of course it will be up to you to treat compiler messages like they matter and work for clean compiles - programs that compile and don't make messages at all.
    Last edited by whiteflags; 06-29-2017 at 03:19 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. ACM Programming Contest.... Seeking Answers!
    By jwroblewski44 in forum C++ Programming
    Replies: 10
    Last Post: 11-18-2014, 11:34 AM
  2. Programming newbie seeking help!
    By jaja009 in forum C Programming
    Replies: 2
    Last Post: 02-19-2010, 12:12 PM
  3. My First Youtube video
    By abachler in forum Tech Board
    Replies: 17
    Last Post: 10-04-2009, 03:02 AM
  4. C++ youtube video
    By kypronite in forum A Brief History of Cprogramming.com
    Replies: 21
    Last Post: 08-18-2008, 11:05 PM
  5. Seeking avice from an expert in programming
    By i_wanna_ in forum C++ Programming
    Replies: 2
    Last Post: 04-08-2005, 01:27 PM

Tags for this Thread