2016-09-15 4 views
0

Я программист Java, пытающийся забрать C. В частности, я пытаюсь понять strcat(). Если я позвоню:c strcat перезаписать исходную строку?

strcat(dst, src); 

Я получаю, что strcat() изменит мою строку dst. Но не следует ли оставить строку src в одиночку? Рассмотрим следующий код:

#include<stdio.h> 
#include<string.h> 

void printStuff(char* a, char* b){ 
     printf("----------------------------------------------\n"); 
     printf("src: (%d chars)\t\"%s\"\n",strlen(a),a); 
     printf("dst: (%d chars)\t\"%s\"\n",strlen(b),b); 
     printf("----------------------------------------------\n"); 
} 

int main() 
{ 
     char src[25], dst[25]; 
     strcpy(src, "This is source123"); 
     strcpy(dst, "This is destination"); 

     printStuff(src, dst); 
     strcat(dst, src); 
     printStuff(src, dst); 

     return 0; 
} 

Который производит этот выход на моей коробке Linux, компиляции с GCC:

---------------------------------------------- 
src: (17 chars) "This is source123" 
dst: (19 chars) "This is destination" 
---------------------------------------------- 
---------------------------------------------- 
src: (4 chars) "e123" 
dst: (36 chars) "This is destinationThis is source123" 
---------------------------------------------- 

Я предполагаю, что полный «Это source123» Строка еще находится в памяти и strcat() переместил указатель char * src вперед на 13 символов. Но почему? Почему 13 символов? Я играл с длиной строки dst, и это определенно влияет на указатель src после выполнения strcat(). Но я не понимаю, почему ...

Также ... как бы вы отлаживали это, в GDB, сказать? Я попробовал «шаг», чтобы войти в функцию strcat(), но я предполагаю, что эта функция не была проанализирована отладчиком; «Шаг» ничего не делал.

Спасибо! -ROA

PS - Однократное замечание. Я прочитал подобные сообщения strcat() на этом сайте, но не увидел тот, который, казалось, напрямую касался моего вопроса. Извиняюсь, если я пропустил пост, который сделал.

+1

«Я предполагаю, что полный» Это источник123 «Строка по-прежнему в памяти» -> возможно. Когда код воспроизводится вне его песочницы (записывать границы внешнего массива), все может случиться - неопределенное поведение (UB). Не ожидайте «Но не стоит ли оставлять строку src?» Результаты и объяснения могут иметь смысл сегодня, но завтра результаты могут отличаться. – chux

+1

Вы только выделили 'char [25]' for 'dst'. Как вы ожидаете, что там будет 37 символов (включая конечный нуль)? – user2357112

+1

'src' +' dst' concatenated имеют больше, чем 24 + 1 символ терминатора, который вы выделили с помощью 'dst [25]' –

ответ

4

У вашего адресата недостаточно памяти для хранения новой конкатенированной строки. В этом случае это означает, что src, вероятно, переписывается strcat из-за того, что он записывается за пределы dst.

Выделите достаточно памяти для dst, и она должна работать без перезаписывания исходной строки. Обратите внимание, что новый сегмент памяти, содержащий конкатенированные строки, должен быть как минимум размером двух строк (в вашем случае 36) плюс пространство для нулевого терминатора.

+4

стоит упомянуть, что такая ошибка является очень распространенной проблемой безопасности, и что ['strncat'] (http://en.cppreference.com/w/c/string/byte/strncat) - это сама ... безопасность поскольку он не ведет себя так, как ожидают люди. – Mgetz

+1

@Mgetz Я бы сказал, что strncat ведет себя так, как и следовало ожидать. Вы смущаете его с помощью strncpy? – hyde

+1

@hyde, я бы сказал, что люди склонны ожидать, что параметр длины, переданный в 'strncat()', представляет собой общий размер целевого буфера или, может быть, менее одного, что было бы разумным дизайном. То, что он вместо этого представляет собой верхнюю границу количества передаваемых символов, не только удивительно, но и сложнее в использовании. –

1

Да, я уверен, что все, что связано с ручным управлением памятью, сопряжено с некоторыми трудностями, если ваш фон строго Java.

Что касается всего, что связано с строками C, вероятно, вам будет полезно выложить все, что вы знаете о Java String s из головы. Ближайшими Java-аналогами строк C являются char[] и byte[]. Однако даже там вы можете столкнуться с проблемами, потому что Java выполняет проверку границ для вас, но C нет. На самом деле, C позволяет вам делать все, что вам не нужно делать, все время отступая и тихо бормоча, «кто знает, что произойдет, если вы это сделаете?» .

В частности, при вызове strcat() или любую другую функцию, которая записывает в char массив, вы несут ответственность за обеспечение того, что есть достаточно места в массиве назначения для размещения символов. Если этого не происходит, то получившееся поведение не определено (кто знает, что произойдет?). Вы осуществляли именно такое неопределенное поведение.

Вообще говоря, вам нужно сделать одну или несколько из этих вещей:

  • есть жесткий верхний предел размера, которые могут быть необходимы, и выделить по крайней мере так много места, или
  • Know сколько места у вас есть, и работать в этом пространстве (например, урезать любой избыток) или
  • Отследите, сколько места у вас есть и сколько места вам нужно, и выделите больше места по мере необходимости (будучи уверенным позже бесплатно все динамически выделяемое пространство, когда оно вам больше не понадобится).