Search code examples
c++stringfor-loopreplacefunction-definition

String subscript out of range in Visual Studio


I've been following Codecademy's course on C++ and I've reached the end but I'm confused with the last task.

We have to create a program which filters chosen words as 'swear words' and replaces them with whichever character chosen.

I have written the code in Visual Studio which can be seen below main.cpp

#include <iostream>
#include <string>

#include "functions.h"


int main()
{
    std::string word = "broccoli";
    std::string sentence = "I sometimes eat broccoli.";
    bleep(word, sentence);

    for (int i = 0; i < sentence.size(); i++) {
        std::cout << sentence[i];
    }

    std::cout << "\n";
}

functions.cpp

#include <iostream>
#include <string>
#include "functions.h"

void asterisk(std::string word, std::string &text, int i) {
    for (int k = 0; k < word.size(); k++) {
        text[i + k] = '*';
    }

}

void bleep(std::string word, std::string &text) {
    for (int i = 0; i < text.size(); i++) {
        int match = 0;
        for (int j = 0; j < word.size(); j++) {
            if (text[i + j] == word[j]) {
                match++;
            }       
            
        }
        if (match == word.size()) {
            asterisk(word, text, i);
        }
    }
}


functions.h

#pragma once

void bleep(std::string word, std::string &text);
void asterisk(std::string word, std::string &text, int i);

Now, when I run this code in Visual Studio, I get an assert relating to string subscript it out of range. But with the same code, it works in Codecademys in browser code editor. I cannot for the life of me work out why it won't run in VS.


Solution

  • This for inner loop

        for (int j = 0; j < word.size(); j++) {
            if (text[i + j] == word[j]) {
                match++;
            }       
            
        }
    

    does not take into account that the tail of the string text can be much less than the value of word.size(). So this for loop provokes access memory outside the string text.

    To avoid this situation at least rewrite the outer loop the following way

    if ( not ( text.size() < word.size() ) )
    {
        for ( size_t i = 0, n = text.size() - word.size() + 1; i < n; i++) {
            //...
    }
    

    A more efficient and safer approach is to use the method find of the class std::string instead of loops.

    Here is a demonstrative program.

    #include <iostream>
    #include <string>
    
    std::string & bleep( std::string &text, const std::string &word, char c )
    {
        if ( auto n = word.size() )
        {
            for ( std::string::size_type pos = 0; 
                  ( pos = text.find( word, pos ) ) != std::string::npos; 
                  pos += n )
            {
                text.replace( pos, n, n, c );
            }
        }
        
        return text;
    }
    
    int main() 
    {
        std::string word = "broccoli";
        std::string sentence = "I sometimes eat broccoli.";
        
        std::cout << sentence << '\n';
        std::cout << bleep( sentence, word, '*' ) << '\n';
        
        return 0;
    }
    

    The program output is

    I sometimes eat broccoli.
    I sometimes eat ********.