Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use memset while handling strings in C++? [closed]

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

like image 882
BhishanPoudel Avatar asked Jun 05 '19 15:06

BhishanPoudel


People also ask

How do you string a memset?

Syntax: void* memset( void* str, int ch, size_t n); Memset() converts the value ch to unsigned char and copies it into each of the first n characters of the object pointed to by str[]. If the object is not trivially-copyable (e.g., scalar, array, or a C-compatible struct), the behavior is undefined.

How does memset work in c?

The C library function void *memset(void *str, int c, size_t n) copies the character c (an unsigned char) to the first n characters of the string pointed to, by the argument str.

Why do we use memset in c?

The memset() function sets the first count bytes of dest to the value c . The value of c is converted to an unsigned character. The memset() function returns a pointer to dest . This example sets 10 bytes of the buffer to A and the next 10 bytes to B.

How do I use memset globally?

memset() is used to fill a block of memory with a particular value. The syntax of memset() function is as follows : // ptr ==> Starting address of memory to be filled // x ==> Value to be filled // n ==> Number of bytes to be filled starting // from ptr to be filled void *memset(void *ptr, int x, size_t n);


4 Answers

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';
}
like image 51
Vlad from Moscow Avatar answered Oct 24 '22 06:10

Vlad from Moscow


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

That's because you overwrote the null terminator.

The terminator is part of the array's size (an array is not magic), though it's not part of the logical string size.

So, I think you meant:

memset(str, 't', strlen(str));
//               ^^^^^^

Error when using "t" with double quotes

Completely different thing. You told the computer to set every character in the string, to a string. Doesn't make sense; won't compile.


How to use memset in C++?

Don't.

Either use the type-safe std::fill, in combination with std::begin and std::end:

std::fill(std::begin(str), std::end(str)-1, 't');

(If you're worried about performance, don't be: this will just delegate to memset where possible via template specialisation, optimisation not required, without sacrificing type-safety; example here in libstdc++.)

Or just a std::string to begin with. 😊


I was learning the fuction memset in C++ from https://www.geeksforgeeks.org/memset-in-cpp/ where the example is given as below

Don't attempt to learn C++ from random websites. Get yourself a good book instead.

like image 33
Lightness Races in Orbit Avatar answered Oct 24 '22 06:10

Lightness Races in Orbit


Vlad has helpfully answered the first part of your question, but I feel like the second part could be explained a little more intuitively:

As others have mentioned, 't' is a character whereas "t" is a string, and strings have a null terminator at the end. This makes "t" an array of not one but two characters - ['t', '\0']! This makes memset's error more intuitive - it can coerce a single char to an int easily enough, but it chokes when it's given an array of chars. Just like in Python, int(['t', '\0']) (or ord(['t', '\0'])) doesn't compute.

like image 5
Valhalla Avatar answered Oct 24 '22 06:10

Valhalla


This is the correct syntax for memset...

void* memset( void* dest, int ch, std::size_t count );

Converts the value ch to unsigned char and copies it into each of the first count characters of the object pointed to by dest. If the object is a potentially-overlapping subobject or is not TriviallyCopyable (e.g., scalar, C-compatible struct, or an array of trivially copyable type), the behaviour is undefined. If count is greater than the size of the object pointed to by dest, the behaviour is undefined.

(source)

For the first syntax memset(str, 't', sizeof(str));. The compiler complained because of extra size. It prints 18 times tttttttttttttt!R@. I suggest try with sizeof(str) -1 for char array.

For Second syntax memset(str, "t", sizeof(str)); you are providing the second parameter is a string. This is the reason compiler complains error: invalid conversion from ‘const char*’ to ‘int’

like image 5
Arun Kumar Avatar answered Oct 24 '22 07:10

Arun Kumar