Alternative of strcpy in c++
Asked Answered
A

4

15

In C i used strcpy to make a deep copy of a string, but is it still 'fine' to use strcpy in C++ or are there better alternatives which i should use instead ?

Anaglyph answered 20/1, 2011 at 19:14 Comment(1)
Is your string null terminated or not? https://mcmap.net/q/333323/-copying-non-null-terminated-unsigned-char-array-to-std-string/176769Haver
C
16

In C++ the easiest way is usually to use the std::string class instead of char*.

#include <string>
...
  std::string a = "Hello.";
  std::string b;
  b = a;

The line "b = a;" does the same thing you would usually do with strcpy.

Carltoncarly answered 20/1, 2011 at 19:17 Comment(14)
Other than that, strcpy is just as legal and dangerous in C++ as it is in C.Tyre
So it basically always makes a deep copy when you assign one string to another variable, ok thanks :)Anaglyph
std::string's operator= does not actually do a deep copy. It does a reference counted copy (and thus, does not do exactly what strcpy does). If you want to make a deep copy of a std::string, you need to call it like so: std::string a = "Hello."; std::string b; b = a.c_str(); to force it to actually make a copy instead of just copying the pointers and incrementing the reference count.Lefevre
@Zac Howland, @Aerus: Not necessarily, and less so now that some time ago. Copy on write is an implementation detail that is allowed but not required by the language. While in the past some libraries used it, the fact is that it is inefficient in multithreaded environments, as it requires locking that will incur a cost regardless of whether copies have been made, making the overall performance worse in most scenarios.Severity
@David: Some implementations have changed this, but others either haven't or require you to set compiler flags telling the compiler which version of std::string to use. Using the c_str() version will work in either case, though.Lefevre
@Zac: I mostly use g++, where strings don't use copy-on-write. I have just looked over the VC2010 headers and the string implementation there does not use copy-on-write either. There is at least one question in SO stating that VC2008 does not have copy-on-write either (or at least not by default). My guess is that the few (if there are any) compilers that still have copy-on-write will remove it in the near future. Now the trend is towards multicore processing, and the expense of locking overweights the advantage of COWSeverity
Since when does g++ NOT use copy-on-write strings? A simple test proves that g++ uses COW. Using c_str() in the assignment is the way to do deep copying since its implementation independent.Lattimore
@JeremyWalton: A simple test shows you're right coliru.stacked-crooked.com/…. That's a bug in the library. As of C++11 that is specifically disallowed.Aloke
@MooingDuck: The standard may have changed, but there are implementations that haven't caught up to the standard yet. If you want to avoid potential issues, using std::string sCopy = sOriginal.c_str();` works in all of the last 3 versions of the C++ standard.Lefevre
@ZacHowland: std::string sCopy(sOrigional.begin(), sOrigional.end()); also works in C++03 and C++11, but is less buggy.Aloke
@MooingDuck: Yes, it does the same thing, but it is no more nor no less buggy unless you are storing null-terminators in your string.Lefevre
@ZacHowland: "It's no more buggy unless..."Aloke
@MooingDuck: Yes, unless you do something that basically makes every other function not work for your entire data set as well.Lefevre
Not sure I like this, I've used copy-on-write is tons of multithreaded applications. You have to be careful just like any other object. It could be made safer, I guess, and without "locking". "In multithreaded systems, COW can be implemented without the use of traditional locking and instead use Compare-and-swap to increment or decrement the internal reference counter." Since by BEING in multiple threads, the string will always have more than one refcount, making the original ALWAYS the same. Any "changes" will create a new array. Incrementing/decrementing the refcount should be safe.Lattimore
L
26

I put this in the comment above, but just to make the code readable:

std::string a = "Hello.";
std::string b;
b = a.c_str();   // makes an actual copy of the string
b = a;           // makes a copy of the pointer and increments the reference count

So if you actually want to mimic the behavior of strcpy, you'll need to copy it using c_str();

UPDATE

It should be noted that the C++11 standard explicitly forbids the common copy-on-write pattern that was used in many implementations of std::string previously. Thus, reference counting strings is no longer allowed and the following will create a copy:

std::string a = "Hello.";
std::string b;
b = a; // C++11 forces this to be a copy as well
Lefevre answered 20/1, 2011 at 19:31 Comment(3)
I don't think you can say "c_str() makes a copy": it only returns the raw char* data. This data is then passed to the std::string constructor which performs the actual copy. If it was not copied, then b would be invalidated as soon as a went of scope or was destroyed.Skimp
I didn't say c_str() makes a copy. I said b = a.c_str() makes a copy of the string. c_str() is not the focus of the operation; the copy-assignment operator is.Lefevre
I asked for an edit to make the question clearer. Feel free to perform a rollback if anything's wrong.Skimp
C
16

In C++ the easiest way is usually to use the std::string class instead of char*.

#include <string>
...
  std::string a = "Hello.";
  std::string b;
  b = a;

The line "b = a;" does the same thing you would usually do with strcpy.

Carltoncarly answered 20/1, 2011 at 19:17 Comment(14)
Other than that, strcpy is just as legal and dangerous in C++ as it is in C.Tyre
So it basically always makes a deep copy when you assign one string to another variable, ok thanks :)Anaglyph
std::string's operator= does not actually do a deep copy. It does a reference counted copy (and thus, does not do exactly what strcpy does). If you want to make a deep copy of a std::string, you need to call it like so: std::string a = "Hello."; std::string b; b = a.c_str(); to force it to actually make a copy instead of just copying the pointers and incrementing the reference count.Lefevre
@Zac Howland, @Aerus: Not necessarily, and less so now that some time ago. Copy on write is an implementation detail that is allowed but not required by the language. While in the past some libraries used it, the fact is that it is inefficient in multithreaded environments, as it requires locking that will incur a cost regardless of whether copies have been made, making the overall performance worse in most scenarios.Severity
@David: Some implementations have changed this, but others either haven't or require you to set compiler flags telling the compiler which version of std::string to use. Using the c_str() version will work in either case, though.Lefevre
@Zac: I mostly use g++, where strings don't use copy-on-write. I have just looked over the VC2010 headers and the string implementation there does not use copy-on-write either. There is at least one question in SO stating that VC2008 does not have copy-on-write either (or at least not by default). My guess is that the few (if there are any) compilers that still have copy-on-write will remove it in the near future. Now the trend is towards multicore processing, and the expense of locking overweights the advantage of COWSeverity
Since when does g++ NOT use copy-on-write strings? A simple test proves that g++ uses COW. Using c_str() in the assignment is the way to do deep copying since its implementation independent.Lattimore
@JeremyWalton: A simple test shows you're right coliru.stacked-crooked.com/…. That's a bug in the library. As of C++11 that is specifically disallowed.Aloke
@MooingDuck: The standard may have changed, but there are implementations that haven't caught up to the standard yet. If you want to avoid potential issues, using std::string sCopy = sOriginal.c_str();` works in all of the last 3 versions of the C++ standard.Lefevre
@ZacHowland: std::string sCopy(sOrigional.begin(), sOrigional.end()); also works in C++03 and C++11, but is less buggy.Aloke
@MooingDuck: Yes, it does the same thing, but it is no more nor no less buggy unless you are storing null-terminators in your string.Lefevre
@ZacHowland: "It's no more buggy unless..."Aloke
@MooingDuck: Yes, unless you do something that basically makes every other function not work for your entire data set as well.Lefevre
Not sure I like this, I've used copy-on-write is tons of multithreaded applications. You have to be careful just like any other object. It could be made safer, I guess, and without "locking". "In multithreaded systems, COW can be implemented without the use of traditional locking and instead use Compare-and-swap to increment or decrement the internal reference counter." Since by BEING in multiple threads, the string will always have more than one refcount, making the original ALWAYS the same. Any "changes" will create a new array. Incrementing/decrementing the refcount should be safe.Lattimore
R
6

If you're using c++ strings, just use the copy constructor:

std::string string_copy(original_string);

Or the assignment operator

string_copy = original_string

If you must use c-style strings (i.e. null-terminated char arrays), then yeah, just use strcpy, or as a safer alternative, strncpy.

Riesman answered 20/1, 2011 at 19:20 Comment(2)
Aah nice alternative, hadn't thought of that :)Anaglyph
strncpy is not a safer alternative to strcpy. Not only does it have several problems of its own, but it's not even intended as a replacement to strcpy, despite the name. strlcpy is a closer match to what strcpy intends, but strcpy_s is likely the safest option.Tammara
L
3

You are suggested to use strcpy_s because in addition to the destination and source arguments, it has an additional argument for the size of the destination buffer to avoid overflow. But this is still probably the fastest way to copy over a string if you are using char arrays/pointers.

Example:

char *srcString = "abcd";
char destString[256];

strcpy_s(destString, 256, srcString);
Lepidosiren answered 20/1, 2011 at 19:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.