Использовать strcpy или нет - PullRequest
2 голосов
/ 11 мая 2019

Если у меня есть массив строк

char* q[2];
q[0] = "Hello";
q[1] = "World";

и я хочу поместить q[0] в новый массив строк

char* x[2];

называется х в х [0], я должен использовать strcpy или только это?

x[0] = q[0];

Кажется, это работает, но в прошлом использование strcpy не вызывало некоторых проблем.

Должен ли я сделать это на всякий случай? В чем разница?

char* a = malloc(strlen(q[0]) + 1);
strcpy(a, q[0]);
x[0] = a;

Ответы [ 4 ]

4 голосов
/ 11 мая 2019

Назначение работает без выделения дополнительной памяти.

Если вы используете strcpy(), вам нужно выделить достаточно памяти для x[0], прежде чем выполнять копирование (strlen(q[0]) + 1 байт минимум), и выдолжны обеспечить его освобождение в надлежащее время.

3 голосов
/ 11 мая 2019

Для ясности, нет ничего "сломанного" в strcpy();если у него «вызвало некоторые проблемы в прошлом» для вас, то есть из-за ошибок в вашем использовании, а не из-за внутренней проблемы с strcpy(), и вам, возможно, следует опубликовать вопрос об этом, такВаши неправильные представления могут быть исправлены.

Семантика между назначением указателя строки и копированием строки различается, поэтому это зависит от того, что вы пытаетесь сделать.

x это массив указателей.Назначение:

x[0] = q[0];

Копирует ли не строку, указанную q[0], в память, указанную x[0].скорее это изменяет значение x[0] на адрес строки, на которую ссылается q[0].Таким образом, они оба ссылаются на одну и ту же строку в памяти.Строковые данные перемещены или скопированы , в данном случае только ссылка на строковые литералы:

q[0] --> "Hello"
          ^
          |
x[0] -----

В этом смысле x не "новый массив строк" , как вы описываете, но массив указателей на одинаковых строк .Возможно, это не семантическое поведение, которое вы намеревались.

Если вы попытаетесь:

strcpy( x[0], q[0] ) ;

Это будет семантическая ошибка, поскольку в ваших фрагментах x[0] не указывает на какую-либо определенную память, поэтому строковые данные, на которые ссылается q[0] будет скопировано в какое-то неопределенное место с неопределенными результатами - ничего хорошего, даже если на первый взгляд будет работать .

Если x[0] ссылается на некоторые из них правильновыделенное пространство, например, либо объявленным как массив таким образом:

char x[2][128] ;

, либо выделенным динамически:

x[0] = malloc( 128 ) ;

, либо первым назначенным в другое допустимое пространство:

char a[120]
x[0] = a ;

Или путем динамического выделения a, как в примере в вашем вопросе.

Тогда strcpy( x[0], q[0] ) является допустимой операцией и скопирует строку, на которую ссылается q[0] в пространство, указанное x[0]:

q[0] --> "Hello"

x[0] --> "Hello"

Критически в этом случае строки не ссылаются на одно и то же пространство в памяти.

Обратите внимание, что если вы действительно хотели присваивание указателя строки semantics (что, вероятно, менее вероятно), а q относится к константам литеральных строк , тогда для более безопасного кода важно объявить массивы как указатели на const data:

const char* q[2] ;
const char* x[2] ;

Массив q должен быть объявлен const в этом случае в любом случае, но ясно, что x не должен быть константой, если вы хотите семантику string-copy .

Полезно, объявив его const, любая попытка strcpy() завершится неудачей детерминистически при компиляции, а не неопределенным, ошибочным и, возможно, скрытым поведением во время выполнения.Таким образом, он применяет предполагаемую семантику, если это действительно то, что вы хотели.Точно так же это останавливает вас от попытки изменить данные, на которые ссылается q[n], что также будет неопределенным поведением (но, как правило, приведет к ошибке времени выполнения в современной настольной ОС).

1 голос
/ 11 мая 2019

У меня есть массив строк

Ну, не совсем. q объявлено как

char* q[2];

Что делает его массивом из двух указателей на char. То, что в C обычно называют строкой , представляет собой массив с нулевым символом в конце char.

Затем каждому элементу массива присваиваются два значения:

q[0] = "Hello";
q[1] = "World";

Где "Hello" и "World" являются строковыми литералами . Здесь не копируется ни одна строка, только указатели.

Позже в коде вы можете переназначить каждый указатель, как в

q[0] = "Bye";

Но любая попытка изменить символы или , освобождая указатели , приведет к трудному обнаружению ошибок.

Было бы безопаснее объявить его в виде массива для указателя на const char, если они должны быть нетронутыми, чтобы в случае ошибок по крайней мере сообщения об ошибках были бы более информативным. Например, 1 или 2 .

Если вам нужны изменяемые строки и контроль времени жизни этих объектов, ваш единственный вариант - выделить достаточно памяти и скопировать строки.

1 голос
/ 11 мая 2019

Нет общего правила, какое из них лучше. Это зависит только от вашего намеренного использования строк.

Простое назначение указателя будет работать, если вы не планируете изменять память самостоятельно.

Если вы освободите первый указатель, у вас останется копия во втором указателе. Но сейчас это недействительно. Кроме того, если вы добавляете или изменяете содержимое с помощью одного указателя, это изменение также будет видно с помощью второго указателя. Это происходит потому, что вы используете одну и ту же память.

Если вы скопируете строку, у вас будет независимый контент. Вы можете освободить или изменить через первый указатель и по-прежнему использовать его через второй указатель. Конечно, вам также необходимо отдельно освободить память, если вы скопировали ее в другое место.

Простое задание, конечно, быстрее. Какой из них лучше, зависит от ваших потребностей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...