Thread: counting characters in array

  1. #1
    Registered User
    Join Date
    Mar 2015
    Posts
    5

    counting characters in array

    I am trying to count the number of characters entered as the name then find the factorial of that number.
    Can anybody fix this code?
    Code:
    #include <stdio.h>#include <stdlib.h>
    #include <string.h>
    
    
    int fact(int m)
    {
    if(m==0) return 1;
    return (m*fact(m-1));
    }
    int main()
    {
    int i, j, N, m;
    int F=1;
    char name[N];
    printf("Enter Last name:\n"); scanf("%s",&name[N]);
    printf("Last name : %s and Permutation is %d\n", name, F);
    
    
    return 0;
    }
    After compiling
    Code:
    Enter Last name:Johnastone
    Last name : Johnastone and Permutation is 0

  2. #2
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    First, make sure your code is neatly formatted and indented:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int fact(int m)
    {
        if(m==0)
            return 1;
    
        return (m*fact(m-1));
    }
    
    int main()
    {
        int i, j, N, m;
        int F=1;
        char name[N];
    
        printf("Enter Last name:\n");
        scanf("%s",&name[N]);
    
        printf("Last name : %s and Permutation is %d\n", name, F);
    
        return 0;
    }
    Secondly, maximize your compiler warnings and heed them. I received several, but one that is very important:

    Code:
    /*
    main.c|17|warning: 'N' is used uninitialized in this function|
    */
    So your array is of an unknown size. "N" is not given a value before used to declare your character array.

    Also, variable-length arrays are only allowed with C99 (or later). And for many applications (including yours), are not really needed. Better to explicitly size your array.

    Code:
    scanf("%s",&name[N]);
    Same problem ... "N" has no value. But it shouldn't be "N" anway. "scanf()" expects a pointer to the first element, so you actually want "&name[0]". But since an array name by itself acts as a pointer to the first element, it can just be:

    Code:
    scanf("%s",name);
    And "scanf()" is not a good way to read a string. First of all, it stops reading after a space. Second of all, it does not (by default) limit the number of characters it tries to read, which could lead to a buffer overflow. There are ways around this with "scanf()", but they are overly complicated.

    Better to just use "fgets()" to read a line of text. Note that "fgets()" stores the newline (if there's space), so this has to be handled as well.

    It's all described here: FAQ > Get a line of text from the user/keyboard (C) - Cprogramming.com

    The last problem I see after a quick look is that you never actually call your function "fact()".

    Fix these things and try your program again. If you get stuck, post your updated code and any questions you have.

  3. #3
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    "scanf()" expects a pointer to the first element
    Minor nitpick, "pointer to the first element" is quite flexible. You don't have to supply the actual first element of the array. It can be a pointer to any element as long as there's sufficient space to store the input string, which provides opportunities for working with a slice of the array:
    Code:
    #include <stdio.h>
    #include <string.h>
    
    int main(void)
    {
        char name[BUFSIZ];
        int n;
    
        fputs("First Name: ", stdout);
        fflush(stdout);
        scanf("%s%n", name, &n);
    
        strcat(name, " ");
    
        fputs("Last Name: ", stdout);
        fflush(stdout);
        scanf("%s", &name[n + 1]);
    
        puts(name);
    
        return 0;
    }
    The reason &name[N] is wrong is because that particular index would be out of bounds (if N were initialized in the first place, of course ).
    My best code is written with the delete key.

  4. #4
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Please also check what the scanf() function returns: it is the number of successful conversions.

    (Except that the standards committee goofed, so now some implementations count the special %n pattern, while others do not. Fortunately, %n never ever assigns a negative value, so you can avoid that by assigning -1 to the int variable before scanning.)

    Also, if you do not specify the maximum length for %s, scanf() might overrun your buffer, and write garbage over your other variables. If you have an array of 50 characters, use %49s; the 50th one is reserved for the NUL character, '\0', that signifies the end of the string in C.

    If other words, use either
    Code:
        char name[100];
        int namelen;
    
        if (scanf("%99s", name) != 1) {
            printf("Hey, no input!\n");
            exit(EXIT_FAILURE);
        }
        namelen = strlen(name);
    or
    Code:
        char name[100];
        int namelen;
    
        namelen = -1;
        (void)scanf("%99s%n", name, &namelen);
        if (namelen < 1) {
            printf("No input!\n");
            exit(EXIT_FAILURE);
        }



    I am literally distraught how tutors and teachers do not bother with the scanf() family of functions' return value. It causes all sorts of weird issues, when the input is unexpected -- say, instead of a zero, you accidentally type in an O, and don't even notice it yourself.

    The easy way is always, always check the number of conversions done. (That abovementioned %n is the annoying exception.)

    For example, if you tell the user to input number pairs, and anything else to end the loop, you can do
    Code:
        double x, y;
    
        while (1) {
            if (scanf(" %lf %lf", &x, &y) != 2) {
                printf("That was not a number pair, so we're done. Thanks anyway!\n");
                break;
            }
    
            /* Do something with x and y */
        }
    See? It's not difficult, and makes your program very robust against incorrect inputs.

    If you wanted to be real nice and friendly with those number pairs, pros do it like this:
    Code:
        char linebuf[128], *line;
        double x, y;
    
        while (NULL != (line = fgets(linebuf, sizeof linebuf, stdin))) {
    
            if (sscanf(line, " %lf %lf", &x, &y == 2 ||
                sscanf(line, " %lf , %lf", &x, &y == 2 ||
                sscanf(line, " [ %lf %lf ]", &x, &y == 2 ||
                sscanf(line, " [ %lf , %lf ]", &x, &y == 2 ||
                sscanf(line, " ( %lf %lf )", &x, &y == 2 ||
                sscanf(line, " ( %lf , %lf )", &x, &y == 2) {
    
                /* You have x and y! */
    
            } else {
                /* This removes trailing newline. It's just a neat trick. */
                line[strcspn(line, "\n")] = '\0';
    
                printf("%s did not contain two numbers.\n", line);
            }
        }
    Unlike plain scanf(), this latter one reads each line into a buffer (assumes less than 127 characters on each line), then tries the scanning pattern (until one works and reads the two numbers), or complains about the line. This can handle many different forms, like [1.2, 5] for example, and you can add any forms you think your users might try by just adding more sscanf() lines. (They are evaluated one by one, until one matches; the rest are then not tried.)

    None of this is advanced stuff. You cannot tack on robustness and error checking after the fact in any kind of reliable manner; you should always think it in, bake it into the recipe, and be happy that you know your program can handle small user errors in a friendly manner.

    Thank you for reading. End of rant.

  5. #5
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    Please also check what the scanf() function returns: it is the number of successful conversions.
    I was wondering how long it would take for someone to mention that.

    If you have an array of 50 characters, use %49s; the 50th one is reserved for the NUL character, '\0', that signifies the end of the string in C.
    Considered and discarded in my example, as it would add unnecessary complication that hides the concept being shown. The problem is two fold. First, I was using a macro for the size of the array, which cannot be used in a format string without some stringizing macro magic or by constructing a format string at runtime. Second, while the first call to scanf would be straightforward enough, the second call needs to account for a smaller buffer size due to the slice. In that case you have little choice but to construct the format string at runtime:
    Code:
    #include <stdio.h>
    #include <string.h>
    
    int main(void)
    {
        char name[BUFSIZ];
        char fmt[BUFSIZ];
        int n;
    
        fputs("First Name: ", stdout);
        fflush(stdout);
        sprintf(fmt, "%%%lus%%n", sizeof name - 1);
        
        if (scanf(fmt, name, &n) == 1 && n < sizeof name - 1)
        {
            size_t remaining = sizeof name - n - 2;
    
            strcat(name, " ");
    
            if (remaining > 0)
            {
                fputs("Last Name: ", stdout);
                fflush(stdout);
                sprintf(fmt, "%%%lus", remaining);
    
                if (scanf(fmt, &name[n + 1]) == 1)
                {
                    puts(name);
                }
            }
        }
    
        return 0;
    }
    That's a lot of fluff for an example only meant to show &name[n + 1] in the second scanf call. And that code still isn't complete as there's potential risk of size_t wrapping and to be strictly robust you'd need to do something about more input than the buffer can hold across two scanf calls as well as general error handling across the board.

    Teaching is a balancing act of keeping things simple enough to digest at the time while maintaining some semblance of correctness, and it's often iterative by adding complexity to improve correctness as the student can handle the adjustment.
    My best code is written with the delete key.

  6. #6
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by Prelude View Post
    Considered and discarded in my example, as it would add unnecessary complication that hides the concept being shown.
    If the pattern makes bugs easy and avoiding bugs hard, I say the pattern is faulty.

    I can see your point, but I don't think it is the right path to show to new programmers.
    I'd much rather have them hardcode their buffer lengths, not just in the variable declarations, but in the pattern scans too.

    The first step forward from scanning tokens would then be using fgets() to read a full line, then sscanf() to parse it. Since the line length is already known, the character arrays to hold the contents only need to be as long, to avoid any buffer overruns.

    The next step from that is either POSIX.1 getline(), self-written variants thereof, and custom input routines with dynamic memory management. One nifty one is a pair of functions to make CSV -- proper CSV, with quoted strings correctly handled -- parsing simple: one parses the next token, including quotes and escapes, into a dynamically allocated/managed buffer, but ends at the end of the record; the other skips to the beginning of next record. I think I've posted those here at some point; they're not that complicated, and completely avoid any kind of line length limitations (except for size_t overflow, I guess).

    This path can be achieved while all the while baking in reasonably robust error checking.

    Quote Originally Posted by Prelude View Post
    That's a lot of fluff for an example
    Yeah, I know, my posts are full of fluff. I just don't think that example shows people a good way to do things.

    Quote Originally Posted by Prelude View Post
    Teaching is a balancing act of keeping things simple enough to digest at the time while maintaining some semblance of correctness, and it's often iterative by adding complexity to improve correctness as the student can handle the adjustment.
    I disagree. Oh, it is absolutely a balancing act, yes. I just don't think you can fix incorrect afterwards by adding more stuff. Just like you cannot add security afterwards either.

    It gets a bit philosophical, but it boils down to the fact that people are lazy, and if they learn to first get results, and then to add stuff to check if that result makes sense, they won't bother. They'll just get the results, and leave it to others to check it makes sense. I hate that, with a passion. Better teach them to consider robustness as an integral part of getting any results.

    Then again, my views are not exactly popular, nor considered practical, so I should probably just shut up with my rants and take a hiatus.

  7. #7
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    Quote Originally Posted by Nominal Animal View Post
    Then again, my views are not exactly popular, nor considered practical, so I should probably just shut up with my rants and take a hiatus.
    No, you should stay

    I for one really like what you bring to cboard.

  8. #8
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Prelude
    Considered and discarded in my example, as it would add unnecessary complication that hides the concept being shown.
    I think that the least we can do is to mention that the return value of scanf should be checked and that the field width for %s should be specified. Of course, people might end up doing what we show as-is rather than going on to do the prose that we say, but at least we will not be remiss in addressing those important issues in some way (though I admit that from time to time I leave such details out).

    Quote Originally Posted by Nominal Animal
    I disagree. Oh, it is absolutely a balancing act, yes. I just don't think you can fix incorrect afterwards by adding more stuff. Just like you cannot add security afterwards either.

    It gets a bit philosophical, but it boils down to the fact that people are lazy, and if they learn to first get results, and then to add stuff to check if that result makes sense, they won't bother. They'll just get the results, and leave it to others to check it makes sense. I hate that, with a passion. Better teach them to consider robustness as an integral part of getting any results.

    Then again, my views are not exactly popular, nor considered practical, so I should probably just shut up with my rants and take a hiatus.
    I think your views with respect to the teaching of beginners are practical in C++, not C. Take your hiatus to read Stroustrup's essay on Learning Standard C++ as a New Language (PDF)
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  9. #9
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    The first step forward from scanning tokens would then be using fgets() to read a full line, then sscanf() to parse it. Since the line length is already known, the character arrays to hold the contents only need to be as long, to avoid any buffer overruns.
    Of course, and that's the recommended approach with the standard library. Though since the code I was commenting on used scanf, my counter example also used scanf in a similar manner to be consistent and facilitate comparison.

    I just don't think that example shows people a good way to do things.
    Slicing an array with *scanf isn't a good way to do things period. So the example was poor by nature, but I felt it important to mention only as a way to broaden one's horizons on the possibilities of arrays. In hindsight, I was too lazy in not checking scanf's return value. But I'll blame that on lack of coffee. ;p

    It gets a bit philosophical, but it boils down to the fact that people are lazy, and if they learn to first get results, and then to add stuff to check if that result makes sense, they won't bother.
    The counter argument being that if what they learn initially is too complex, they'll discard it in favor of a simpler solution found elsewhere that fails to iteratively teach improvements. Or they'll simply stop learning, and the pedagogy fails flat out. In my experience, truly lazy people will figure out the worst possible solution regardless of what they're taught.

    We largely agree in principle, and I welcome any additions you have to examples or explanations I give that cover more bases than was my intention. That's one of the biggest benefits of the forum: one person answers and others can add on. The student can only benefit.
    My best code is written with the delete key.

  10. #10
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    I just don't think you can fix incorrect afterwards by adding more stuff. Just like you cannot add security afterwards either.
    O_o

    I'd much rather have them hardcode their buffer lengths, not just in the variable declarations, but in the pattern scans too.
    o_O

    Teaching people to start with a lot of magic numbers generally falls into the "they won't bother" category of learning to do things the right way.

    Then again, my views are not exactly popular, nor considered practical, so I should probably just shut up with my rants and take a hiatus.
    Who cares? What difference does it make if an opinion isn't popular?

    Come to think on it, didn't you fuss about not liking forums based in popularity the last time you left?

    If you post a view people disagree with, the people who disagree with you will share their own view.

    Given enough information, visitors can judge for themselves what works.

    *shrug*

    Feel free to leave, but you should look for a better reason than expressing an unpopular opinion.

    Soma
    “Salem Was Wrong!” -- Pedant Necromancer
    “Four isn't random!” -- Gibbering Mouther

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. counting characters in an array?
    By vsovereign in forum C Programming
    Replies: 3
    Last Post: 06-07-2010, 07:52 AM
  2. help counting characters
    By dom89 in forum C++ Programming
    Replies: 7
    Last Post: 12-08-2007, 10:25 PM
  3. Counting Characters
    By jrdoran in forum C Programming
    Replies: 2
    Last Post: 11-28-2006, 08:06 PM
  4. Counting Characters HELP !!
    By Zozo17 in forum C Programming
    Replies: 2
    Last Post: 03-01-2005, 08:00 PM
  5. Counting characters
    By tay_highfield in forum C Programming
    Replies: 3
    Last Post: 01-29-2003, 12:54 PM

Tags for this Thread