2016-01-15 2 views
-1

Я написал программу для копирования strings. Я получил неожиданно выход.В чем разница между этими двумя строковыми копиями C/C++

КОД

tper *mem = new tper[MAX_NUM_USERS]; 

и тперь выглядит как

typedef struct tper { 
    int8 UserId; 
    char *b[MAX_NUM_START_LOC]; 
} tper; 

/* PROBLEM */ 

strncpy(mem->b[num], sourceIds[num]->source, STRING_SIZE); 
strcpy(mem->b[num], sourceIds[num]->source); 

mem->b[num] = sourceIds[num]->source; 

Первые две копии аварии на runtime. Третий экземпляр работает. Я не понимаю, почему? Оба значения: char *.

Я что-то упустил?

+0

Мы не можем отлаживать эти 3 строки кода, есть десятки вещей, которые могут привести к их сбою. Можете ли вы как-то понять, если 'mem-> b [num]' указывает на буфер, который достаточно велик, чтобы содержать строку, которую вы хотите скопировать? – nos

+1

Не используйте 'strncpy', пока вы не прочтете свою документацию тщательно и не поймете, что она на самом деле делает. Это не ** «безопасная» замена для «strcpy». –

+0

@PeteBecker: Я бы стал более строгим: НЕ ИСПОЛЬЗУЙТЕ 'strncpy'. Внимательно прочитайте его документацию и посмотрите, что он на самом деле делает. Это не «безопасная» замена для «strcpy», она громоздка и подвержена ошибкам, всегда есть лучшая альтернатива. – chqrlie

ответ

1

Возможно, ваш mem->b[num] не указывает на буфер, достаточно большой для хранения строки.

Возможно, указатель mem->b[num] не инициализирован вообще, или указатель NULL.

Вы отвечаете за то, что mem->b[num] указывает на допустимый буфер, который достаточно велик, чтобы содержать строку, которую вы хотите скопировать там, если вы используете strcpy/strncpy - эти функции не «создают» новую строку, они просто копирует байты из одного места в другое, и вам решать, чтобы другое место было допустимой памятью, в которой могут храниться байты.

strncpy

strncpy (мем-> Ь [Num], sourceIds [число] -> источника, STRING_SIZE);

Это копирует все, байт за байтом, вплоть до 0 байт из sourceIds[num]->source в буфер, который mem->b[num] указывает.

Затем он заполняет оставшиеся байты mem->b[num] до STRING_SIZE с 0 байт.

Примечание:

  • если mem->b[num] фактически не имеет номер, по крайней мере, STRING_SIZE, strncpy будет копировать данные в конце прошлого mem->b[num]
  • если sourceIds[num]->source имеет ровно STRING_SIZE нет места для в 0 байт в mem->b[num], а данные в mem->b[num] не соответствуют фактической строке. В таком случае вам может понадобиться вручную обрезать строку, иначе другой код, который обрабатывает его как строку, может проходить за ее пределами. Например.do:

    mem-> b [num] [STRING_SIZE - 1] = 0;

зЬгсру

зЬгсру (мем-> Ь [Num], sourceIds [число] -> источника);

Это копирует все до сих включительно 0 байт из sourceIds[num]->source в mem->b[num].

  • Примечание: если mem->b[num] фактически не имеет номера, по крайней мере strlen(sourceIds[num]->source) + 1, зЬгср будет копировать данные в конце прошлого mem->b[num]

назначение Указателя

mem->b[num]= sourceIds[num]->source; 

это просто ЦЕССИОНАРИИ mem->b[num], чтобы указать на то, что sourceIds[num]->source указывает на. Никакая часть строки не копируется. Если вы измените что-либо внутри буфера, на которое указывает sourceIds[num]->source, вы увидите то же самое при использовании mem->b[num], так как они оба указывают на одно и то же.

+0

обновленный вопрос с распределением памяти. Я не могу понять, что не так – user437777

+0

@ user437777 Где код, который присваивает некоторую действительную память вашему 'mem-> b [num]'? Ни одна из ваших опубликованных строк кода не делает этого. – nos

+0

tper * mem = new tper [MAX_NUM_USERS]; Нужно ли инициализировать это? Я могу скопировать идентификатор пользователя, но строковые копии копирования – user437777

2

strcpy а также strncpy копирует побайтовые данные из исходного буфера в буфер назначения. Таким образом, в первую очередь размер буфера назначения должен соответствовать всем копирующим данным. Это, конечно, предполагает, что оба буфера указывают на допустимые области памяти. Во-вторых, исходный буфер должен быть NULL-terminated, за исключением того, что вы хотите копировать меньшее количество байтов, чем оно содержит. Пожалуйста, проверьте, все ли в вашем случае в порядке.

+0

У меня есть правильный размер буфера. Он падает. Почему работает третий случай? – user437777

+1

Я предполагаю, что 'mem-> b [num]' является указателем на строку C. Затем эта операция просто изменяет значение указателя, чтобы указать на исходную строку. В этом случае копирование данных не выполняется. – dmi

+0

спасибо. В чем проблема с первым. копирование двух символов char * strings – user437777

0

Если вы сделали это в последовательности

tper *mem = new tper[MAX_NUM_USERS]; 
strncpy(mem->b[num], sourceIds[num]->source, STRING_SIZE); 
strcpy(mem->b[num], sourceIds[num]->source); 

ваша проблема заключается в том, что распределение mem не устанавливает какой-либо из элементов массива mem->b, чтобы указать на что-нибудь. Элементами являются неинициализированные указатели, поэтому доступ к их значениям (не говоря уже о разыменовании их, что и есть strncpy() и strcpy() do) дает неопределенное поведение.

Если выделить память для элемента mem->b к точке на ....

tper *mem = new tper[MAX_NUM_USERS]; 
for (int i = 0; i < MAX_NUM_START_LOC; ++i) 
    mem->b[i] = new char[STRING_SIZE]; 

strncpy(mem->b[num], sourceIds[num]->source, STRING_SIZE); 
strcpy(mem->b[num], sourceIds[num]->source); 

вы найдете это работает, если sourceIDs[num] не является NULL, sourceIds[num]->source точки на (первый элемент) массив от char, и (для звонка strcpy()), если strlen(sourceIds[num]->source) < STRING_SIZE. Я предполагаю, что num находится между 0 и MAX_NUM_START_LOC-1.

Причина: mem->b[num] = sourceIds[num]->source работает, потому что это (предположительно) простое назначение указателя. Он не копирует строку из sourceIds[num]->source в mem->b[num].

Смежные вопросы