Традиционный подход заключается в тестировании вашего проекта в виртуальной среде:
> conda install
requirements.txt
с помощью команды OP Это создаст изолированный requirements.txt
.
См. Также pipenv
и poetry
. Эти инструменты автоматически создают виртуальные среды вашего проекта и надежно отслеживают ваши зависимости.
Существует три типа поведения стандартов, которым необходимо интересоваться.
1/Определенное поведение. Это будет работать над всеми соответствующими реализациями. Используйте это свободно.
2/определенное Реализацией поведение. Как указано, это зависит от реализации, но по крайней мере это все еще определяется. Реализации требуются, чтобы документ, что они делают в этих случаях. Используйте это, если Вы не заботитесь о мобильности.
3/Неопределенное поведение. Что-либо может произойти. И мы имеем в виду что-либо, до и включая Ваш весь компьютер, выходящий из строя в явную особенность и глотающий себя, Вас и значительную долю Ваших коллег. Никогда не используйте это. Когда-либо! Серьезно! Не заставляйте меня приехать туда.
Копирование больше, что 4 символа и нулевой байт к a char[5]
неопределенное поведение.
Серьезно, не имеет значения, почему Ваша программа отказывает с 14 символами, но не 13, Вы почти наверняка перезаписываете некоторую не отказывающую информацию о стеке, и Ваша программа, скорее всего, приведет к неправильным результатам так или иначе. На самом деле катастрофический отказ лучше с тех пор, по крайней мере, он останавливает Вас полагающийся на возможно плохие эффекты.
Увеличьте размер массива к чему-то более подходящему (char[14]
в этом случае с доступной информацией) или использование некоторая другая структура данных, которая может справиться.
Обновление:
Так как Вы кажетесь настолько обеспокоенными обнаружением, почему дополнительные 7, символы не вызывают проблемы, но 8 символов, делают, давайте предусмотрим возможное расположение стека на вводе main()
. Я говорю "возможный", так как фактическое расположение зависит от соглашения о вызовах, которое использует Ваш компилятор. Так как C запускают вызовы кода main()
с argc
и argv
, стек в начале main()
, после выделения места для a char[5]
, мог быть похожим на это:
+------------------------------------+
| C start-up code return address (4) |
| argc (4) |
| argv (4) |
| x = char[5] (5) |
+------------------------------------+
Когда Вы пишете байты Hello1234567\0
с:
strcpy (x, "Hello1234567");
кому: x
, это перезаписывает argc
и argv
но, по возврату из main()
, это хорошо. Конкретно Hello
заполняет x
, 1234
заполняет argv
и 567\0
заполняет argc
. Если Вы на самом деле не пытаетесь использовать argc
и/или argv
после этого Вы будете хорошо:
+------------------------------------+ Overwrites with:
| C start-up code return address (4) |
| argc (4) | '567<NUL>'
| argv (4) | '1234'
| x = char[5] (5) | 'Hello'
+------------------------------------+
Однако, если Вы пишете Hello12345678\0
(отметьте дополнительное "8") к x
, это перезаписывает argc
и argv
и также один байт обратного адреса так, чтобы, когда main()
попытки возвратиться к C запускают код, он уходит в волшебную землю вместо этого:
+------------------------------------+ Overwrites with:
| C start-up code return address (4) | '<NUL>'
| argc (4) | '5678'
| argv (4) | '1234'
| x = char[5] (5) | 'Hello'
+------------------------------------+
Снова, это зависит полностью от соглашения о вызовах Вашего компилятора. Возможно, что другой компилятор всегда увеличивал бы массивы к нескольким 4 байтам, и код не перестанет работать там, пока Вы не записали еще три символа. Даже тот же компилятор может выделить переменные на стековом фрейме по-другому, чтобы гарантировать, что выравнивание удовлетворено.
Это - то, под чем они подразумевают неопределенный: Вы не знаете то, что собирается произойти.
Вы копируете в стек, таким образом, это зависит от того, что компилятор поместил в стек, поскольку, сколько дополнительных данных потребуется, чтобы разрушать Вашу программу.
Некоторые компиляторы могли бы произвести код, который откажет только с единственным байтом по размеру буфера - это не определено, каково поведение.
Я предполагаю, что размера 13 достаточно для перезаписи обратного адреса или чего-то подобного, которое отказывает, когда функция возвращается. Но другой компилятор или другая платформа могли / катастрофический отказ с другой длиной.
Также Ваша программа могла бы отказать с другой длиной, если бы она работала в течение более длительного времени, если что-то менее важное перезаписывалось.
Это зависит от того, что находится на стеке после массива "ул.". Вы просто, оказывается, не топчете что-либо критическое, пока Вы не копируете это много символов.
Таким образом, это собирается зависеть от того, что еще находится в функции, компилятор, Вы используете и возможно параметры компилятора также.
13 5 + 8, предполагая, что существует два некритических слова после массива ул., затем что-то критическое (возможно, обратный адрес)
Для 32-разрядной платформы Intel объяснение следующее. Когда Вы объявляете символ [5] на стеке, компилятор действительно выделяет 8 байтов из-за выравнивания. Затем это типично для функций, чтобы иметь следующий пролог:
push ebp
mov ebp, esp
это сохраняет ebp значение реестра на стеке, затем перемещается особенно, значение регистра в ebp для использования особенно оценивают для доступа к параметрам. Это приводит к еще 4 байтам на стеке, который будет занят значением ebp.
В эпилоге восстанавливается ebp, но его значение обычно только используется для доступа к выделенным стеку параметрам функции, так перезаписи, это не может причинить боль в большинстве случаев.
Таким образом, у Вас есть следующее расположение (стек растет вниз на Intel): 8 байтов для Вашего массива, затем 4 байта для ebp, затем обычно обратный адрес.
Поэтому необходимо перезаписать по крайней мере 13 байтов для катастрофического отказа программы.
Добавить к вышеупомянутым ответам: можно протестировать на ошибки как они с инструментом, такие как Valgrind. Если Вы находитесь в Windows, взглянули на это, ТАК распараллельте.
Это - чистая красота неопределенного поведения (UB): это не определено.
Ваш код:
char str[5];
strcpy(str,"Hello12345678");
Записи 14 байтов/символы к str
который может только содержать 5 байтов/символы. Это вызывает UB.
Q: Итак, почему это не отказывает для "Hello1234567" и только отказывает для "Hello12345678" т.е. строки с длиной 13 или больше, чем 13.
Поскольку поведение не определено. Используйте strncpy. Посмотрите эту страницу http://en.wikipedia.org/wiki/Strcpy для получения дополнительной информации.
strncpy небезопасен, так как он не добавляет ПУСТОЕ завершение, если исходная строка имеет длину> = n, где n является размером целевой строки.
char s[5];
strncpy(s,5,"test12345");
printf("%s",s); // crash
Мы всегда используем strlcpy для облегчения этого.