Memset Structure with Std::String Contained

memset structure with std::string contained

No, you cant, it would overwrite the internal state of the string and make bad things happen. You could wrap all the POD stuff in a seperate struct and put that in your current one, that way you could memset that and let the string default construct.

Edit: Just to clarify, the string will almost certainly be storing a pointer to the memory its allocated for storage. The string's constructor will always have run before you can memset it (even if you memset this in the constructor of your type, the string constructor would run first). So you would be overwriting this pointer value, and instead of pointing to its storage, it would a pointer to NULL, or some other almost definitely invalid value.

Is it okay to memset a struct which has an another struct with Smart pointer member?

No, never use memset on any structure which is not POD.

However your code isn't doing that, it is only calling memset on _School which is POD as it only contains a pointer, calling memset on _Class or _Globals would have undefined behaviour. However I'd prefer removing the memset and adding a constructor to _School which initialises pSchool to nullptr:

struct _School {
_Class *pSchool;
_School() : pSchool(nullptr) {}
};

You need to use new in C++ code rather than malloc as malloc doesn't call class constructors.

Also note that identifiers starting with underscore followed by an uppercase character are reserved for use by the compiler/standard library.

The complete code would be:

#include <map>
#include <string>
#include <memory>
#include <iostream>

struct Globals{
std::shared_ptr<std::map<int, std::string> > rollNamePair;
};

struct Class {
Globals Globals; // global vars
};

struct School {
Class *pSchool;
School() :pSchool(nullptr) {}
};

int main()
{
School abc;
abc.pSchool= new Class();
abc.pSchool->Globals.rollNamePair = std::make_shared<std::map<int, std::string> >();
(*abc.pSchool->Globals.rollNamePair)[1] = "John";
(*abc.pSchool->Globals.rollNamePair)[2] = "Paul";

std::cout << (*abc.pSchool->Globals.rollNamePair)[1] << "\n";
std::cout << (*abc.pSchool->Globals.rollNamePair)[2];

delete abc.pSchool;

return 0;
}

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

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';
}

memset() or value initialization to zero out a struct?

Those two constructs a very different in their meaning. The first one uses a memset function, which is intended to set a buffer of memory to certain value. The second to initialize an object. Let me explain it with a bit of code:

Lets assume you have a structure that has members only of POD types ("Plain Old Data" - see What are POD types in C++?)

struct POD_OnlyStruct
{
int a;
char b;
};

POD_OnlyStruct t = {}; // OK

POD_OnlyStruct t;
memset(&t, 0, sizeof t); // OK as well

In this case writing a POD_OnlyStruct t = {} or POD_OnlyStruct t; memset(&t, 0, sizeof t) doesn't make much difference, as the only difference we have here is the alignment bytes being set to zero-value in case of memset used. Since you don't have access to those bytes normally, there's no difference for you.

On the other hand, since you've tagged your question as C++, let's try another example, with member types different from POD:

struct TestStruct
{
int a;
std::string b;
};

TestStruct t = {}; // OK

{
TestStruct t1;
memset(&t1, 0, sizeof t1); // ruins member 'b' of our struct
} // Application crashes here

In this case using an expression like TestStruct t = {} is good, and using a memset on it will lead to crash. Here's what happens if you use memset - an object of type TestStruct is created, thus creating an object of type std::string, since it's a member of our structure. Next, memset sets the memory where the object b was located to certain value, say zero. Now, once our TestStruct object goes out of scope, it is going to be destroyed and when the turn comes to it's member std::string b you'll see a crash, as all of that object's internal structures were ruined by the memset.

So, the reality is, those things are very different, and although you sometimes need to memset a whole structure to zeroes in certain cases, it's always important to make sure you understand what you're doing, and not make a mistake as in our second example.

My vote - use memset on objects only if it is required, and use the default initialization x = {} in all other cases.

memset is causing a crash on std::string assignments

You're overwriting deviceId's internal data by doing memset all over it; don't ever do memset over anything but a POD data type. This is C++, we have constructors. Your code should look something like this:

struct DeviceParams
{
int deviceCount;

struct DeviceNode
{
DeviceNode() : eTransportType() { } // initialise eTransportType
// to 0, deviceId initialises itself

static const int MAX_DEVICE_ID = 256;
static const int MAX_DEVICE_ENTRIES = 10;

std::string deviceId; // Device name to Open
TransportType eTransportType;
} deviceNodes[DeviceNode::MAX_DEVICE_ENTRIES];
};

Then

DeviceParams Param;

// get a pointer to Param in pParam

pParam->deviceNodes[index].deviceId = "some string";


Related Topics



Leave a reply



Submit