16

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,030
  • 21
  • 100
  • 124
Cannoliopsida
  • 2,919
  • 4
  • 33
  • 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

25

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);    

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);

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);

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
  • 94,180
  • 52
  • 233
  • 338
  • 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
  • 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
  • @R.MartinhoFernandes Well, seems like not anymore: https://en.cppreference.com/w/c/experimental/dynamic/strdup – Ayxan Haqverdili Jan 06 '20 at 12:55
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
  • 242,813
  • 27
  • 432
  • 630
3

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,030
  • 21
  • 100
  • 124
sirgeorge
  • 6,083
  • 1
  • 26
  • 33
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,001
  • 3
  • 43
  • 70