Путаница в "функции strcat в C предполагает, что строка назначения достаточно велика для хранения содержимого исходной строки и ее собственной".
Итак, я прочитал это strcat
Функция должна использоваться осторожно, так как строка назначения должна быть достаточно большой, чтобы содержать содержимое своей собственной и исходной строки. И это было верно для следующей программы, которую я написал:
#include <stdio.h>
#include <string.h>
int main(){
char *src, *dest;
printf("Enter Source String : ");
fgets(src, 10, stdin);
printf("Enter destination String : ");
fgets(dest, 20, stdin);
strcat(dest, src);
printf("Concatenated string is %s", dest);
return 0;
}
Но не верно для того, что я написал здесь:
#include <stdio.h>
#include <string.h>
int main(){
char src[11] = "Hello ABC";
char dest[15] = "Hello DEFGIJK";
strcat(dest, src);
printf("concatenated string %s", dest);
getchar();
return 0;
}
Эта программа заканчивает тем, что добавляет оба, не считая, что строка назначения недостаточно велика. Почему это так?
5 ответов
strcat()
Функция действительно должна использоваться осторожно, потому что она не защищает вас от чего-либо. Если исходная строка не заканчивается NULL, строка назначения не заканчивается NULL или в строке назначения не хватает места, strcat
все равно будет копировать данные. Поэтому легко перезаписать данные, которые вы не хотели перезаписывать. Вы несете ответственность за то, чтобы у вас было достаточно места. С помощью strncat()
вместо strcat
также даст вам дополнительную безопасность.
Редактировать Вот пример:
#include <stdio.h>
#include <string.h>
int main()
{
char s1[16] = {0};
char s2[16] = {0};
strcpy(s2, "0123456789abcdefOOPS WAY TOO LONG");
/* ^^^ purposefully copy too much data into s2 */
printf("-%s-\n",s1);
return 0;
}
Я никогда не назначал s1
поэтому в идеале вывод должен быть --
, Однако из-за того, как компилятор оказался устроен s1
а также s2
в памяти, вывод, который я на самом деле получил, был -OOPS WAY TOO LONG-
, strcpy(s2,...)
переписал содержимое s1
также.
На gcc, -Wall
или же -Wstringop-overflow
поможет вам обнаружить ситуации, подобные этой, когда компилятор знает размер исходной строки. Однако, в общем, компилятор не может знать, насколько большими будут ваши данные. Следовательно, вы должны написать код, который гарантирует, что вы не скопируете больше, чем у вас есть место.
strcat
Функция не может точно знать, какова длина буфера назначения, поэтому она предполагает, что переданный ей буфер достаточно велик. Если это не так, вы вызываете неопределенное поведение, записывая после конца буфера. Вот что происходит во втором куске кода.
Первый фрагмент кода также недействителен, потому что оба src
а также dest
неинициализированные указатели. Когда вы передаете их fgets
, он читает любое содержащееся в нем значение мусора, обрабатывает его как действительный адрес, а затем пытается записать значения в этот неверный адрес. Это также неопределенное поведение.
Одна из вещей, которая делает C быстрым, заключается в том, что он не проверяет соблюдение правил. Он просто говорит вам правила и предполагает, что вы следуете им, и если вы не делаете ничего плохого, это может произойти или не произойти. В вашем конкретном случае это сработало, но это не гарантировано.
Например, когда я запустил ваш второй кусок кода, он также работал. Но если я изменил это на это:
#include <stdio.h>
#include <string.h>
int main(){
char dest[15] = "Hello DEFGIJK";
strcat(dest, "Hello ABC XXXXXXXXXX");
printf("concatenated string %s", dest);
return 0;
}
Программа вылетает.
Я думаю, что ваша путаница на самом деле не об определении strcat
, Ваша настоящая путаница заключается в том, что вы предполагали, что компилятор C будет применять все "правила". Это предположение совершенно неверно.
Да, первый аргумент strcat
должен быть указателем на память, достаточную для хранения сцепленного результата. В обеих ваших программах это требование нарушено. Из-за отсутствия сообщений об ошибках в обеих программах у вас может сложиться впечатление, что, возможно, это правило не то, о чем вы думали, что как-то его можно вызвать strcat
даже когда первый аргумент не является указателем на достаточное количество памяти. Но нет, дело не в этом: звоню strcat
когда не хватает памяти, безусловно, неправильно. Тот факт, что не было сообщений об ошибках или что одна или обе программы "работали", ничего не доказывает.
Вот аналогия. (Возможно, у вас даже был такой опыт, когда вы были ребенком.) Предположим, ваша мать говорит вам не перебегать улицу, потому что вас может сбить машина. Предположим, вы все равно перебегаете улицу и не попадаете под машину. Вы пришли к выводу, что совет вашей матери был неверным? Это верный вывод?
Таким образом, то, что вы прочитали, было правильно: strcat
должны быть использованы осторожно. Но давайте перефразируем это: вы должны быть осторожны при звонке strcat
, Если вы не будете осторожны, все вещи могут пойти не так, без предупреждения. На самом деле, многие руководства по стилю рекомендуют не использовать такие функции, как strcat
вообще, потому что ими так легко злоупотреблять, если ты небрежен. (Функции, такие как strcat
может использоваться совершенно безопасно, если вы осторожны - но, конечно, не все программисты достаточно осторожны.)
Оба фрагмента вызывают неопределенное поведение - первый, потому что src
а также dest
не инициализируются так, чтобы указывать куда-либо значимое, и второе, потому что вы пишете за концом массива.
C не требует какой-либо проверки границ при доступе к массиву - вы не получите исключение "Индекс вне диапазона", если попытаетесь записать после конца массива. Вы можете получить ошибку времени выполнения, если попытаетесь получить доступ за границей страницы или всплыть что-то важное, например, указатель кадра, но в противном случае вы просто рискуете повредить данные в своей программе.
Да, вы несете ответственность за то, чтобы целевой буфер был достаточно большим для конечной строки. В противном случае результаты непредсказуемы.
Я хотел бы указать, что на самом деле происходит во 2-й программе, чтобы проиллюстрировать проблему.
Он выделяет 15 байтов в ячейке памяти, начиная с dest, и копирует в нее 14 байтов (включая нулевой терминатор):
char dest[15] = "Hello DEFGIJK";
... и 11 байтов в src с 10 байтами, скопированными в него:
char src[11] = "Hello ABC";
Затем вызов strcat() копирует 10 байтов (9 символов плюс нулевой терминатор) из src в dest, начиная сразу после 'K' в dest. Результирующая строка в dest будет иметь длину 23 байта, включая нулевой терминатор. Проблема в том, что вы выделили только 15 байт в dest, и память, прилегающая к этой памяти, будет перезаписана, то есть повреждена, что приведет к нестабильности программы, неверным результатам, повреждению данных и т. Д.
Обратите внимание, что функция strcat() ничего не знает об объеме памяти, выделенной вами в dest (или src, если на то пошло). Вам нужно убедиться, что вы выделили достаточно памяти в dest, чтобы предотвратить повреждение памяти.
Кстати, первая программа вообще не выделяет память на dest или src, поэтому ваши вызовы fgets() повреждают память, начиная с этих мест.