Schiele
Schiele

Reputation: 125

Count the number of vowels in a series of strings in C

I'm writing a program in C which counts the numbers of the vowels in a series of strings decided by the user, even the number of elements of the strings is decided by the user. The problem is that the function gives always the same numbers of vowels which is the one of the first string.

Here's the code:

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

int vowels(char str[]);

int main(){
    int n, i, x;

    printf("How many strings do you want to insert?");
    scanf("%d", &n);

    printf("How many characters per string?");
    scanf("%d", &x);
    char str[x];

    if(x < 10){
        for(i = 0; i < n; i++){
            printf("Insert a string:");
            scanf("%s", str);

            if(strlen(str) == x){
                vowels(str);
            }
            else{
                printf("Error\n");
            }
        }
    }
    else{
        printf("Error: the number of characters must be < 10");
    }

    return 0;
}

int vowels(char str[]){
    int i, j;

    while(str[i] != '\0'){
        if(str[i] == 'a' || str[i] == 'A' || str[i] == 'e' || 
           str[i] == 'E' || str[i] == 'i' || str[i] == 'I' || 
           str[i] == 'o' || str[i] == 'O' || str[i] == 'u' || 
           str[i] == 'U'){
            j++; }
    i++;
    }

    printf("Number of vowels in the string:%d\n", j); 

return 0;
}

Upvotes: 0

Views: 1160

Answers (3)

Roberto Caboni
Roberto Caboni

Reputation: 7490

Your code contains two mistakes:

  1. i and j indexes of vowels() function are not initialized, so they contain garbage and will lead to an unpredictable behavior of your loops. That's because local variables, unlike global variables, are not initialized by default to 0.

Use instead

int i=0, j=0;
  1. The buffer char str[x]; will not be able to contain a string with x characters. In fact, the space required by string terminator character '\0'.

So it should be

char str[x+1];

With these changes your code will work.

But...

... but it is not enough. In fact, when you get the input string, you don't perform any check to the number of characters retreived:

scanf("%s", str);

By inserting a very long string you will go out of bounds far beyond the str string, causing undefined behavior and, likely, a program crash.

How to fix it? Since your input string can have a maximum length of 9, just define a fixed length array:

char str[11];

Why size 11? First of all your array must contain the maximum of 9 characters and the terminator. So 10. But it has also to contain an extra character, so that inputs longer that 9 characters can be detected.

After that, just receive input string with

scanf("%10s", str);

In this way all legit input string can be stored (from size 1 to size 9). All strings long 10 or more characters will be truncated to a ten chars long string, and your length check will fail as expected.

Please note that in case of long string all characters exceeding the first 10 will remain unread in the stdin buffer. So in case of error you need a mechanism to consume those chars, otherwise you will find them at the next scanf. I've written this trick:

while( fgets( str, 10, stdin ) != NULL )
{
    if( strlen(str) < 10 )
        break;
}

The resulting code, with all the changes I described, is the following one:

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

int vowels(char str[]);

int main(){
    int n, i, x;

    printf("How many strings do you want to insert?");
    scanf("%d", &n);

    printf("How many characters per string?");
    scanf("%d", &x);

    if(x < 10){
        char str[11];
        for(i = 0; i < n; i++){
            printf("Insert a string:");
            scanf("%10s", str);

            if(strlen(str) == x){
                vowels(str);
            }
            else{
                printf("Error\n");
                while( fgets( str, 10, stdin ) != NULL )
                    if( strlen(str) < 10 )
                        break;
            }
        }
    }
    else{
        printf("Error: the number of characters must be < 10");
    }

    return 0;
}

int vowels(char str[]){
    int i=0, j=0;

    while(str[i] != '\0')
    {
        if(str[i] == 'a' || str[i] == 'A' || str[i] == 'e' || 
           str[i] == 'E' || str[i] == 'i' || str[i] == 'I' || 
           str[i] == 'o' || str[i] == 'O' || str[i] == 'u' || 
           str[i] == 'U')
        {
            j++;
        }
    i++;
    }

    printf("Number of vowels in the string:%d\n", j); 

    return 0;
}

Upvotes: 1

user3386109
user3386109

Reputation: 34839

The problems in the vowels function are caused by uninitialized variables. Neither i nor j is given an initial value. A good compiler will generate a warning about this. If you're compiling with gcc or clang, be sure to compile with -Wall -Wextra. Then read the warnings, and fix all of the warnings.

The variable i can be declared and initialized with a for loop, as shown below. The variable j should be given a more descriptive name, like count, and initialized before the loop. You might also want to return the count from the vowels function, and let main do the printing. That way, you can reuse the vowels function in a different program that needs to count vowels, but doesn't want the count printed.

int vowels(char str[]){
    int count = 0;

    for (int i=0; str[i] != '\0'; i++){
        if(str[i] == 'a' || str[i] == 'A' || str[i] == 'e' || 
           str[i] == 'E' || str[i] == 'i' || str[i] == 'I' || 
           str[i] == 'o' || str[i] == 'O' || str[i] == 'u' || 
           str[i] == 'U'){
             count++; 
        }
    }

    return count;
}

The other problem in the program is that the str array is too small. A C string uses a zero byte (known as the NUL terminator) to mark the end of the string. So, for example, if the strlen of a string is 5, then the array holding the string must be at least 6 bytes, 5 for the string, plus one for the NUL.

In your program, you limit the string length to a number less than 10, so you could just declare the str array with a fixed size, e.g. char str[16], and it will always be big enough. OTOH, the scanf doesn't limit the number of characters written into the string, unless you tell it to. The code below shows how to limit the number of characters that scanf writes into the string.

int main(){
    int n, x;

    printf("How many strings do you want to insert?");
    scanf("%d", &n);

    printf("How many characters per string?");
    scanf("%d", &x);
    char str[16];

    if(x < 10){
        for(int i = 0; i < n; i++){
            printf("Insert a string:");
            if (scanf("%15s", str) != 1) {
                printf("That wasn't a valid input\n");
                break;
            }
            else if(strlen(str) == x){
                int count = vowels(str);
                printf("Number of vowels in the string:%d\n", count); 
            }
            else{
                printf("Error\n");
                break;
            }
        }
    }
    else{
        printf("Error: the number of characters must be < 10");
    }

    return 0;
}

Upvotes: 1

Michael Dorgan
Michael Dorgan

Reputation: 12515

I'm pasting your code and adding comments inline. Note: I am not fixing anything, just pointing things out for you to fix.

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

int vowels(char str[]);

int main(){
  int n, i, x;

  printf("How many strings do you want to insert?");
  scanf("%d", &n);  // Consider a more descriptive variable name than 'n'

  printf("How many characters per string?");
  scanf("%d", &x);  // Consider a more descriptive name here too.
  char str[x];  // Note, a string needs 1 extra character for the NUL character.

  if(x < 10){  // 10 is a very magic number.  Consider making a constant.  
    for(i = 0; i < n; i++){
      printf("Insert a string:");
      scanf("%s", str);

      if(strlen(str) == x){
        vowels(str);
      }
      else{
        printf("Error\n"); // Consider breaking or returning here if error...
      }
    }
  }
  else{
    printf("Error: the number of characters must be < 10");
  }

  return 0;
}

int vowels(char str[]){  // Do you need a return value?  What does it represent?
  int i, j;

  while(str[i] != '\0'){  // i and j are not initialized (to 0).  Turning on compiler warnings would catch this.  
//  a for() loop may make more sense
    if(str[i] == 'a' || str[i] == 'A' || str[i] == 'e' || 
       str[i] == 'E' || str[i] == 'i' || str[i] == 'I' || 
       str[i] == 'o' || str[i] == 'O' || str[i] == 'u' || 
       str[i] == 'U'){
      j++; }
    i++;
  }

  printf("Number of vowels in the string:%d\n", j); 

  return 0;  // Again, maybe this function returns void if we don't need a return.
}

Upvotes: 1

Related Questions