0

I am writing a program to return first occurrence of the character and the frequency of that character in the string. For loop in the function is executing infinite times and if condition and block is not executing even once.

What is the problem?

string::size_type find_ch(string &str,char ch,int& i_r)
{
    string::size_type first=0;
    for(auto i=str.size()-1;i>=0;i--)
    {
        cout<<"\nInside a for loop."<<endl;
        if(str[i]==ch)
        {
            cout<<"Inside if."<<endl;
            first=i+1;
            i_r++;
        }
    }
    return first;
}
  • *I am writing a program to return first occurrence of the character and the frequency of that character* -- My question is why are you looping backwards to accomplish this? – PaulMcKenzie Apr 23 '20 at 16:26
  • @PaulMcKenzie Because if I go forward how can I remember occurrence of first character? – Mrugesh Raulji Apr 23 '20 at 16:36

3 Answers3

2

This loop:

for(auto i = str.size() - 1; i>=0; i--)

will only exit when i is less than 0. But this is not a valid value for an unsigned int. The value will wrap to the maximum unsigned int, and you get an infinite loop.

Note that .size() on a std::string returns a size_t, which is basically an unsigned int type.

One way to fix this would be to cast the return type of .size() to an int, like this:

for(auto i = static_cast<int>(str.size()) - 1; i>=0; i--)

Note that it's important to do the cast before subtracting 1, otherwise you'll get the wrong answer when str is empty.

In c++20, you can avoid this issue entirely by calling the std::ssize() free function, which returns a signed version of the size.

cigien
  • 57,834
  • 11
  • 73
  • 112
0

The function definition in general is wrong.

For example if the given character is nit found then why does the function return 0 that is a valid position?

Returning the value first=i+1; will only confuse users of the function. The function shall return std::string::npos if the given character is not found.

Also it is entirely unclear why the loop starts from the end of the string while you need to return the first position of the character.

As for the infinite loop then in the loop there is used variable i that has the unsigned integer type std::string::size_type a value of which never can be negative.

for(auto i=str.size()-1;i>=0;i--)
    ^^^^^^^^^^^^^^^^^^^

That is the condition i >= 0 is always true by the definition.

The function should be defined the following way

std::pair<std::string::size_type, std::string::size_type> find_ch( const std::string &str, char ch )
{
    auto n = str.find( ch );

    std::pair<std::string::size_type, std::string::size_type> p( n, 0 );

    if ( n != std::string::npos )
    {
        ++p.second;
        while ( ( n = str.find( ch, n + 1 ) ) != std::string::npos ) ++p.second;
    } 

    return p;
}

Here is a demonstrative program.

#include <iostream>
#include <string>
#include <utility>

std::pair<std::string::size_type, std::string::size_type> find_ch( const std::string &str, char ch )
{
    auto n = str.find( ch );

    std::pair<std::string::size_type, std::string::size_type> p( n, 0 );

    if ( n != std::string::npos )
    {
        ++p.second;
        while ( ( n = str.find( ch, n + 1 ) ) != std::string::npos ) ++p.second;
    } 

    return p;
}

int main() 
{
    std::string s( "C++ is not the same as C" );

    auto p = find_ch( s, 'C' );

    if ( p.first != std::string::npos )
    {
        std::cout << p.first << ": " << p.second << '\n';
    }

    return 0;
}

The program output is

0: 2

If you are not allowed to use methods of the class std::string then just substitute calls of the method find in the function above to while loops as it is shown below.

#include <iostream>
#include <string>
#include <utility>

std::pair<std::string::size_type, std::string::size_type> find_ch( const std::string &str, char ch )
{
    std::pair<std::string::size_type, std::string::size_type> p( std::string::npos, 0 );

    std::string::size_type n = 0;

    while ( n < str.size() && str[n] != ch ) ++n;

    if ( n != str.size() )
    {
        p.first = n;
        ++p.second;
        while ( ++n != str.size() )
        {
            if( str[n] == ch ) ++p.second;
        }           
    } 

    return p;
}

int main() 
{
    std::string s( "C++ is not the same as C" );

    auto p = find_ch( s, 'C' );

    if ( p.first != std::string::npos )
    {
        std::cout << p.first << ": " << p.second << '\n';
    }

    return 0;
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • As you can see If I start iteration from beginning how can I find the first occurrence of the character at the end. I have not used any functions by my choice. And I have done type casting for `for(auto i = static_cast(str.size()) - 1; i>=0; i--)`. And also I have not mentioned the whole program in which I have made a function to check whether character exist or not. – Mrugesh Raulji Apr 23 '20 at 17:45
  • @MrugeshRaulji It is a bad idea to cast to the type int the unsigned type std::string::size_type. For example an object of the type int is unable to accommodate all values of the type std::string::size_type. – Vlad from Moscow Apr 23 '20 at 17:48
  • OK if I don't cast and use int then can try to make the better function without calling any other function. – Mrugesh Raulji Apr 23 '20 at 18:06
  • @MrugeshRaulji In fact there is no "other functions. There is used the method find of the class std::string that you are already using. – Vlad from Moscow Apr 23 '20 at 18:09
  • Sorry I was talking about `str.find()` and `npos`. – Mrugesh Raulji Apr 23 '20 at 18:16
  • @MrugeshRaulji As I said find is a method of the class std::string. If you are using the class why may not you use its methods? As for npos then it is defined as unsigned -1. See my updated post. npos you can substitute for str.size(). That is if the character is not found then the returned position equal to the length of the string. – Vlad from Moscow Apr 23 '20 at 18:18
  • It is not about why not use the methods. It is about you can do it without the method or not? That's the thing. – Mrugesh Raulji Apr 23 '20 at 18:31
  • @MrugeshRaulji I showed how to do this using loops. – Vlad from Moscow Apr 23 '20 at 18:31
  • And there is one more problem in my program that is when I use the value of `i_r` in the main function it shows zero. Can you tell what is the problem? – Mrugesh Raulji Apr 23 '20 at 18:36
  • @MrugeshRaulji I showed you how the function should be declared. Your function declarations is bad. – Vlad from Moscow Apr 23 '20 at 18:44
0

Here is an answer similar to @Vlad From Moscow, but uses string functions, and the algorithm std::count.

#include <algorithm>
#include <string>
#include <utility>
#include <iostream>

std::pair<int,int> find_ch(const std::string &str, char ch)
{
    std::pair<int, int> ret;
    auto pos = str.find_first_of(ch);
    if ( pos == std::string::npos )
       return {-1,0};  // not found
    return { pos, std::count(str.begin() + pos, str.end(), ch) };
}

int main()
{
   auto pr = find_ch("abccabc", 'b');
   std::cout << "character b is at position " <<  pr.first << ".  Character count is  " << pr.second << "\n";
   pr = find_ch("abccabc", 'c');
   std::cout << "character c is at position " <<  pr.first << ".  Character count is  " << pr.second;
}

Output:

character b is at position 1.  Character count is  2
character c is at position 2.  Character count is  3

Each line of the function basically describes what is being done:

find_first_of the character in the string. If found then return that position and the std::count of that character starting at the first occurrence.

Note the brevity and self-documented way the function is written. A C++ programmer could look at that code and immediately know what it does, due to the names of the functions that are being called.

Writing loops going backwards (as you originally did) with variables incremented here and there, the programmer has to sit down and go through the code to figure out what it is doing, and what the purpose of the function is.

PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45