20

Is there an easy way to copy C strings?

I have const char *stringA, and I want char *stringB to take the value (note that stringB is not const). I tried stringB=(char*) stringA, but that makes stringB still point to the same memory location, so when stringA later changes, stringB does too.

I've also tried strcpy(stringB,stringA), but it seems that if stringB wasn't initialized to a large enough array, there's a segfault. I'm not super experienced with C strings though, am I missing something obvious?

If I just initialize stringB as char *stringB[23], because I know I'll never have a string longer than 22 characters (and allowing for the null terminator), is that the right way? If stringB is checked for equality with other C-strings, will the extra space affect anything?

(And just using strings isn't a solution here, as I need minimal overhead and easy access to individual characters.)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Cannoliopsida
  • 3,044
  • 5
  • 36
  • 61
  • 7
    Re: "and just using strings isn't a solution here, as I need minimal overhead and easy access to individual characters": I strongly suspect that you're overestimating the overhead; and you're certainly overestimating the difficulty of access to individual characters: `c = s[10]` and `s[10] = c` both work just as well if `s` is a `std::string` as if it's a `char*`. – ruakh Mar 06 '12 at 23:59
  • Please format your question. You can use backticks to denote code inline and indent by four spaces to have blocks of code. – 0xC0000022L Mar 07 '12 at 00:01
  • 1
    Use `std::string` unless you've got a better reason than that not to. – bames53 Mar 07 '12 at 00:31
  • 5
    std::string is the solution. You are laboring under the false assumption that it will be more expensive. – Martin York Mar 07 '12 at 00:32
  • Sorry about the formatting; I couldn't figure out what backticks meant (apostrophes don't seem to do anything). – Cannoliopsida Mar 07 '12 at 00:56
  • I'm competing with my friends to write the fastest code, and we're competing on the level of 1/1000s of a second. Will string vs char* make a difference there, or is it really that trivial? – Cannoliopsida Mar 07 '12 at 00:57
  • If you aren't already able to just drop down to ASM and write it that way, you almost certainly aren't going to beat `std::string` while also actually writing **correct** code. – Karl Knechtel Mar 07 '12 at 01:51
  • Part of our competition is to use only C++ without touching the assembly. Good to know that there's so little overhead, thank you! – Cannoliopsida Mar 07 '12 at 17:34

4 Answers4

27

You could use strdup() to return a copy of a C-string, as in:

#include <string.h>

const char *stringA = "foo";
char *stringB = NULL;

stringB = strdup(stringA);
/* ... */
free(stringB);
stringB = NULL; 

You could also use strcpy(), but you need to allocate space first, which isn't hard to do but can lead to an overflow error, if not done correctly:

#include <string.h>

const char *stringA = "foo";
char *stringB = NULL;

/* you must add one to cover the byte needed for the terminating null character */
stringB = (char *) malloc( strlen(stringA) + 1 ); 
strcpy( stringB, stringA );
/* ... */
free(stringB);
stringB = NULL;

If you cannot use strdup(), I would recommend the use of strncpy() instead of strcpy(). The strncpy() function copies up to — and only up to — n bytes, which helps avoid overflow errors. If strlen(stringA) + 1 > n, however, you would need to terminate stringB, yourself. But, generally, you'll know what sizes you need for things:

#include <string.h>

const char *stringA = "foo";
char *stringB = NULL;

/* you must add one to cover the byte needed for the terminating null character */
stringB = (char *) malloc( strlen(stringA) + 1 ); 
strncpy( stringB, stringA, strlen(stringA) + 1 );
/* ... */
free(stringB);
stringB = NULL;

I think strdup() is cleaner, myself, so I try to use it where working with strings exclusively. I don't know if there are serious downsides to the POSIX/non-POSIX approach, performance-wise, but I am not a C or C++ expert.

Note that I cast the result of malloc() to char *. This is because your question is tagged as a c++ question. In C++, it is required to cast the result from malloc(). In C, however, you would not cast this.

EDIT

There you go, there's one complication: strdup() is not in C or C++. So use strcpy() or strncp() with a pre-sized array or a malloc-ed pointer. It's a good habit to use strncp() instead of strcpy(), wherever you might use that function. It will help reduce the potential for errors.

Alex Reynolds
  • 95,983
  • 54
  • 240
  • 345
  • 5
    `strdup` is in POSIX, not C nor C++. – R. Martinho Fernandes Mar 07 '12 at 00:17
  • 1
    `strdup()` is barely more than a wrapper for `strlen()`, `malloc`, and `memset`. I say, if you are doing this as a learning exercise, use `strlen()` and `memset` so you can learn how things work in the scary, powerful world of directly editing memory :) – Robert Martin Mar 07 '12 at 01:35
  • 1
    No, don't use `strncpy` instead of `strcpy`. `strncpy` was created for inserting strings into other strings. As such, it does not null terminate the string. That is trading one problem for another of equal magnitude. – Ed S. Dec 04 '13 at 03:54
  • 1
    @R.MartinhoFernandes Well, seems like not anymore: https://en.cppreference.com/w/c/experimental/dynamic/strdup – Aykhan Hagverdili Jan 06 '20 at 12:55
  • @EdS. I was going to say the same thing. It's unsafe. The man page in linux even states this. As long as you check the length first it's fine. If not something is wrong anyway as non NUL terminated strings is a very bad thing. The advice is well intended but it always misses the problem with not NUL terminating the string. – Pryftan Jun 11 '23 at 16:23
  • Although it's just an example the answer could be improved by setting the pointers to NULL after freeing them. Not doing it is asking for trouble. But `strncpy()` is problematic as it can end up not NUL terminating the string which is very bad indeed. Better to use `strdup()` or else make sure to allocate enough space or make sure it's NUL terminated explicitly. But not having enough space creates another problem namely that it might not be the complete string which is also a potential problem. – Pryftan Jun 11 '23 at 16:30
4

If you want to do it in pure C style then:

char* new_string = strdup(old_string);
free(new_string);

If you want to do it in (kind-of) C++ style:

char* new_string = new char[strlen(old_string) + 1];
strcpy(new_string, old_string);
delete[] new_string;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
sirgeorge
  • 6,331
  • 1
  • 28
  • 33
4

If I just initialize stringB as char *stringB[23], because I know I'll never have a string longer than 22 characters (and allowing for the null terminator), is that the right way?

Almost. In C, if you know for sure that the string will never be too long:

char stringB[MAX+1];
assert(strlen(stringA) <= MAX));
strcpy(stringB, stringA);

or, if there's a possibility that the string might be too long:

char stringB[MAX+1];
strncpy(stringB, stringA, MAX+1);
if (stringB[MAX] != '\0') {
    // ERROR: stringA was too long.
    stringB[MAX] = '\0'; // if you want to use the truncated string
}

In C++, you should use std::string, unless you've proved that the overhead is prohibitive. Many implementations have a "short string optimisation", which will avoid dynamic allocation for short strings; in that case, there will be little or no overhead over using a C-style array. Access to individual characters is just as convenient as with a C-style array; in both cases, s[i] gives the character at position i as an lvalue. Copying becomes stringB = stringA; with no danger of undefined behaviour.

If you really do find that std::string is unusable, consider std::array<char,MAX+1>: a copyable class containing a fixed-size array.

If stringB is checked for equality with other C-strings, will the extra space affect anything?

If you use strcmp, then it will stop at the end of the shortest string, and will not be affected by the extra space.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
1

You're probably looking for strncpy, which allows you to copy the first n characters from a string. Just be sure to add the null-terminator at position n of the copied-to string.

Ken Wayne VanderLinde
  • 18,915
  • 3
  • 47
  • 72