Правильный способ копирования C-строк - PullRequest
11 голосов
/ 07 марта 2012

Есть ли простой способ скопировать C-строки?

У меня есть const char *stringA, и я хочу, чтобы char *stringB принял значение (обратите внимание, что stringB не const).Я попытался stringB=(char*) stringA, но это заставляет stringB по-прежнему указывать на ту же область памяти, поэтому, когда stringA меняется позже, stringB тоже.

Я также пытался strcpy(stringB,stringA), ноПохоже, что если stringB не был инициализирован для достаточно большого массива, это ошибка.Я не очень опытный с C-струнами, я пропускаю что-то очевидное?Если я просто инициализирую stringB как char *stringB[23], потому что я знаю, что у меня никогда не будет строки длиннее 22 символов (и с учетом нулевого терминатора), это правильный путь?Если stringB проверяется на равенство с другими C-строками, повлияет ли дополнительное пространство на что-нибудь?

(и простое использование строк здесь не является решением, так как мне нужны минимальные издержки и легкий доступ к отдельным символам) * * тысяча двадцать-один

Ответы [ 4 ]

17 голосов
/ 07 марта 2012

Вы можете использовать strdup() для возврата копии C-строки, например:

#include <string.h>

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

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

Вы также можете использовать strcpy(), но вам нужно сначала выделить место, что не сложно сделать, но может привести к ошибке переполнения, если не все сделано правильно:

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

Если вы не можете использовать strdup(), я бы порекомендовалиспользование strncpy() вместо strcpy().Функция strncpy() копирует до - и только до - n байтов, что помогает избежать ошибок переполнения.Однако, если strlen(stringA) + 1 > n, вам придется прекратить stringB самостоятельно.Но, как правило, вы будете знать, какие размеры вам нужны для вещей:

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

Мне кажется, strdup() чище, поэтому я стараюсь использовать его для работы исключительно со строками.Я не знаю, есть ли серьезные недостатки в подходе POSIX / non-POSIX, с точки зрения производительности, но я не эксперт C или C ++.

Обратите внимание, что я приведу результат malloc() кchar *.Это потому, что ваш вопрос помечен как c++ вопрос.В C ++ требуется привести результат из malloc().В C, однако, вы бы не разыгрывали это.

РЕДАКТИРОВАТЬ

Итак, есть одно осложнение: strdup() не вC или C ++.Поэтому используйте strcpy() или strncp() с массивом предварительно заданного размера или с указателем malloc.Хорошей привычкой является использование strncp() вместо strcpy(), где бы вы ни использовали эту функцию.Это поможет снизить вероятность ошибок.

3 голосов
/ 07 марта 2012

Если я просто инициализирую stringB как char * stringB [23], потому что я знаю, что у меня никогда не будет строки длиной более 22 символов (и с учетом нулевого терминатора), это правильный путь?

Почти. В Си, если вы точно знаете, что строка никогда не будет слишком длинной:

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

или, если есть вероятность, что строка может быть слишком длинной:

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
}

В C ++ вы должны использовать std::string, если только вы не доказали, что накладные расходы чрезмерны. Многие реализации имеют «оптимизацию коротких строк», которая позволит избежать динамического выделения коротких строк; в этом случае при использовании массива в стиле C накладные расходы будут незначительными или не будут превышены. Доступ к отдельным символам так же удобен, как и в массиве в стиле C; в обоих случаях s[i] дает символ в позиции i как lvalue . Копирование становится stringB = stringA; без опасности неопределенного поведения.

Если вы действительно обнаружите, что std::string непригоден для использования, рассмотрите std::array<char,MAX+1>: копируемый класс, содержащий массив фиксированного размера.

Если строка B проверяется на равенство с другими C-строками, повлияет ли дополнительное пространство на что-либо?

Если вы используете strcmp, он остановится в конце самой короткой строки и не будет затронут дополнительным пробелом.

2 голосов
/ 07 марта 2012

Если вы хотите сделать это в чистом C-стиле, чем:

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

Если вы хотите сделать это в (вроде) стиле C ++:

char* new_string = new char[strlen(old_string) + 1];
strcpy(new_string,old_string);
delete[] new_string;
1 голос
/ 07 марта 2012

Вы, вероятно, ищете strncpy, который позволяет скопировать первые n символов из строки. Просто обязательно добавьте нулевой терминатор в позицию n скопированной строки.

...