Search code examples
c++initializationc-stringsstring-literalsmemset

How to use memset while handling strings in C++?


I am from Python background and recently learning C++. I was learning a C/C++ function called memset and following the online example from website https://www.geeksforgeeks.org/memset-in-cpp/ where I got some compilation errors:

/**
 * @author      : Bhishan Poudel
 * @file        : a02_memset_geeks.cpp
 * @created     : Wednesday Jun 05, 2019 11:07:03 EDT
 * 
 * Ref: 
 */

#include <iostream>
#include <vector>
#include <cstring>

using namespace std;

int main(int argc, char *argv[]){
    char str[] = "geeksforgeeks";

    //memset(str, "t", sizeof(str));
    memset(str, 't', sizeof(str));

    cout << str << endl;

    return 0;
}

Error when using single quotes 't'
This prints extra characters.

tttttttttttttt!R@`

Error when using "t" with double quotes

$ g++ -std=c++11 a02_memset_geeks.cpp 
a02_memset_geeks.cpp:17:5: error: no matching function for call to 'memset'
    memset(str, "t", sizeof(str));
    ^~~~~~
/usr/include/string.h:74:7: note: candidate function not viable: no known
      conversion from 'const char [2]' to 'int' for 2nd argument
void    *memset(void *, int, size_t);
         ^
1 error generated.

How to use the memset in C++ ?

Further Study
Excellent tutorial with shortcomings of memset is given here: https://web.archive.org/web/20170702122030/https:/augias.org/paercebal/tech_doc/doc.en/cp.memset_is_evil.html


Solution

  • This declaration

    char str[] = "geeksforgeeks";
    

    declares a character array that contains a string that is a sequence of characters including the terminating zero symbol '\0'.

    You can imagine the declaration the following equivalent way

    char str[] = 
    { 
        'g', 'e', 'e', 'k', 's', 'f', 'o', 'r', 'g', 'e', 'e', 'k', 's', '\0'
    };
    

    This call of the function memset

    memset(str, 't', sizeof(str));
    

    overrides all characters of the array including the terminating zero.

    So the next statement

    cout << str << endl;
    

    results in undefined behavior because it outputs characters until the terminating zero is encountered.

    You could write instead

    #include <iostream>
    #include <cstring>
    
    int main()
    {
        char str[] = "geeksforgeeks";
    
        std::memset( str, 't', sizeof( str ) - 1 );
        
        std::cout << str << '\n';
    }
    

    Or the following way

    #include <iostream>
    #include <cstring>
    
    int main()
    {
        char str[] = "geeksforgeeks";
    
        std::memset( str, 't', std::strlen( str ) );
        
        std::cout << str << '\n';
    }
    

    That is keeping the terminating zero unchanged in the array.

    If you want to override all characters of the array including the terminating zero, then you should substitute this statement

    std::cout << str << '\n';
    

    for this statement

    std::cout.write( str, sizeof( str ) ) << '\n';
    

    as it is shown in the program below because the array now does not contain a string.

    #include <iostream>
    #include <cstring>
    
    int main()
    {
        char str[] = "geeksforgeeks";
    
        std::memset( str, 't', sizeof( str ) );
        
        std::cout.write( str, sizeof( str ) ) << '\n';
    }
    

    As for this call

    memset(str, "t", sizeof(str));
    

    then the type of the second argument (that is the type const char *) does not correspond to the type of the second function parameter that has the type int. See the declaration of the function

    void * memset ( void * ptr, int value, size_t num );
    

    Thus the compiler issues an error message.

    Apart from character arrays (that are used very often even in C++) you can use also the standard class std::string (or std::basic_string) that simulates strings.

    In this case there is no need to use the standard C function memset to fill a string with a single character. The simplest way to do this is the following

    #include <iostream>
    #include <string>
    
    int main()
    {
        std::string s( "geeksforgeeks" );
        
        s.assign( s.length(), 't' );
        
        std::cout << s << '\n';
    }
    

    Another way is to use the standard algorithm std::fill or std::fill_n declared in the header <algorithm>. For example

    #include <iostream>
    #include <string>
    #include <iterator>
    #include <algorithm>
    
    int main()
    {
        std::string s( "geeksforgeeks" );
        
        std::fill( std::begin( s ), std::end( s ), 't' );
        
        std::cout << s << '\n';
    }
    

    or

    #include <iostream>
    #include <string>
    #include <iterator>
    #include <algorithm>
    
    int main()
    {
        std::string s( "geeksforgeeks" );
        
        std::fill_n( std::begin( s ), s.length(), 't' );
        
        std::cout << s << '\n';
    }
    

    You even can use the method replace of the class std::string one of the following ways

    #include <iostream>
    #include <string>
    
    int main()
    {
        std::string s( "geeksforgeeks" );
        
        s.replace( 0, s.length(), s.length(), 't' );
        
        std::cout << s << '\n';
    }
    

    Or

    #include <iostream>
    #include <string>
    
    int main()
    {
        std::string s( "geeksforgeeks" );
        
        s.replace( std::begin( s ), std::end( s ), s.length(), 't' );
        
        std::cout << s << '\n';
    }