5

I wrote a piece of code to count how many 'e' characters are in a bunch of words.

For example, if I type "I read the news", the counter for how many e's are present should be 3.

#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    char s[255],n,i,nr=0;
    cin.getline(s,255);

    for(i=1; i<=strlen(s); i++)
    {
        if(s[i-1]=='e') nr++;
    }
    cout<<nr;

    return 0;
}

I have 2 unclear things about characters in C++:

  1. In the code above, if I replace strlen(s) with 255, my code just doesn't work. I can only type a word and the program stops. I have been taught at school that strlen(s) is the length for the string s, which in this case, as I declared it, is 255. So, why can't I just type 255, instead of strlen(s)?

  2. If I run the program above normally, it doesn't show me a number, like it is supposed to do. It shows me a character (I believe it is from the ASCII table, but I'm not sure), like a heart or a diamond. It is supposed to print the number of e's from the words.

Can anybody please explain these to me?

Bill Hileman
  • 2,798
  • 2
  • 17
  • 24
PaulP1
  • 125
  • 1
  • 7
  • `cin.getline()` stops reading when it encounters a newline (`char` with value `'\n'`) and appends a trailing zero (`'\0'`) at that point in the array, `strlen(s)` returns the index of the first `char` found with value zero (`'\0'`). It doesn't return the number of elements in the array `s`. Since `s` is uninitialised before calling `cin.getline()`, the characters after the zero are uninitialised. Changing `strlen(s)` to `255` causes the loop to access those uninitialised values, which gives undefined behaviour. – Peter Sep 28 '18 at 14:27

4 Answers4

5
  1. strlen(s) gives you the length of the string held in the s variable, up to the first NULL character. So if you input "hello", the length will be 5, even though s has a capacity of 255....
  2. nr is displayed as a character because it's declared as a char. Either declare it as int, for example, or cast it to int when cout'ing, and you'll see a number.
Ian
  • 1,221
  • 1
  • 18
  • 30
3

strlen() counts the actual length of strings - the number of real characters up to the first \0 character (marking end of string).

So, if you input "Hello":

sizeof(s) == 255
strlen(s) == 5

For second question, you declare your nr as char type. std::cout recognizes char as a single letter and tries it print it as such. Declare your variable as int type or cast it before printing to avoid this.

int nr = 42;
std::cout << nr; 
//or
char charNr = 42;
std::cout << static_cast<int>(charNr);
Yksisarvinen
  • 18,008
  • 2
  • 24
  • 52
2

Additional mistakes not mentioned by others, and notes:

  • You should always check whether the stream operation was successful before trying to use the result.
  • i is declared as char and cannot hold values greater than 127 on common platforms. In general, the maximum value for char can be obtained as either CHAR_MAX or std::numeric_limits<char>::max(). So, on common platforms, i <= 255 will always be true because 255 is greater than CHAR_MAX. Incrementing i once it has reached CHAR_MAX, however, is undefined behavior and should never be done. I recommend declaring i at least as int (which is guaranteed to have sufficient range for this particular use case). If you want to be on the safe side, use something like std::ptrdiff_t (add #include <cstddef> at the start of your program), which is guaranteed to be large enough to hold any valid array size.
  • n is declared but never used. This by itself is harmless but may indicate a design issue. It can also lead to mistakes such as trying to use n instead of nr.
  • You probably want to output a newline ('\n') at the end, as your program's output may look odd otherwise.
  • Also note that calling a potentially expensive function such as strlen repeatedly (as in the loop condition) can have negative performance implications (strlen is typically an intrinsic function, though, and the compiler may be able to optimize most calls away).
  • You do not need strlen anyway, and can use cin.gcount() instead.
  • Nothing wrong with return 0; except that it is redundant – this is a special case that only applies to the main function.

Here's an improved version of your program, without trying to change your code style overly much:

#include <iostream>
#include <cstring>
#include <cstddef>
using namespace std;
int main()
{
    char s[255];
    int nr=0;
    if ( cin.getline(s,255) )
    { // only if reading was successful
        for(int i=0; i<cin.gcount(); i++)
        {
            if(s[i]=='e') nr++;
        }
        cout<<nr<<'\n';
    }

    return 0;
}

For exposition, the following is a more concise and expressive version using std::string (for arbitrary length input), and a standard algorithm. (As an interviewer, I would set this, modulo minor stylistic differences, as the canonical answer i.e. worth full credit.)

#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s;
    if ( getline(cin, s) )
    {
        cout << std::count(begin(s), end(s), 'e') << '\n';
    }
}
Arne Vogel
  • 6,346
  • 2
  • 18
  • 31
1

I have 2 unclear things about characters in C++: 1) In the code above, if I replace the "strlen(s)" with 255, my code just doesn't work, I can only type a word and the program stops, and I have been taught at school that "strlen(s)" is the length for the string s, wich in this case, as I declared it, is 255. So, why can't I just type 255, instead of strlen(s);

That's right, but strings only go the null terminator, even if there's more space allocated. Consider this, per example:

char buf[32];
strcpy(buf, "Hello World!");

There's 32 chars worth of space, but my string is only 12 characters long. That's why strlen returns 12 in this example. It's because it doesn't know how long the buffer is, it only knows the address of the string and parses it until it finds the null terminator.

So if you enter 255, you're going past what was set by cin and you'll read the rest of the buffer. Which, in this case, is uninitialized. That's undefined behavior - in this case it will most likely read some rubbish values, and those might coincidentally have the 'e' value and thus give you a wrong result.

2) If you run the program above normaly, it doesn't show you a number, like it's supposed to do, it shows me a character(I believe it's from the ASCII table but I'm not sure), like a heart or a diamond, but it is supposed to print the number of e's from the words. So can anybody please explain these to me?

You declared nr as char. While that can indeed hold an integer value, if you print it like this, it will be printed as a character. Declare it as int instead or cast it when you print it.

Blaze
  • 16,736
  • 2
  • 25
  • 44