Есть три типа поведения стандартов, вы должны быть заинтересованы.
1/Определено поведение. Это будет работать на всех выполняемых реализациях. Используйте это свободно.
2/Поведение при реализации. Как было сказано, это зависит от реализации, но по крайней мере оно все еще определено. Реализации должны документировать, что они делают в этих случаях. Используйте это, если вы не заботитесь о переносимости.
3/Неопределенное поведение. Все может случиться.И мы имеем в виду что-нибудь, вплоть до вашего компьютера, включая ваш общий компьютер и сворачивающийся, и вы проглатываете себя, вы и большая часть ваших товарищей по работе. Никогда не используйте это. Когда-либо! Шутки в сторону! Не заставляй меня приходить туда.
Копирование более 4 символов и нулевого байта в char[5]
- это неопределенное поведение.
Серьезно, не имеет значения, почему ваша программа вылетает с 14 символами, но не 13, вы почти наверняка перезаписываете какую-то нерушимую информацию о стеке, и ваша программа, скорее всего, произведет неверные результаты. На самом деле, крах лучше, поскольку по крайней мере он останавливает вас, полагаясь на, возможно, плохие последствия.
Увеличьте размер массива до более подходящего (char[14]
в этом случае с доступной информацией) или используйте другую структуру данных, которая может справиться.
Update:
Поскольку вы, кажется настолько озабочены выяснить, почему дополнительные 7 символов не вызывает проблем, но 8 символов делает, давайте предусмотреть возможное расположение стека на входе main()
. Я говорю «возможно», поскольку фактический макет зависит от соглашения о вызовах, которое использует ваш компилятор. Поскольку код запуска C вызывает main()
с argc
и argv
, стек в начале main()
, после выделения места для 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'
+------------------------------------+
Опять же, это полностью зависит от вызывающего соглашения вашего компилятора. Возможно, другой компилятор всегда выгружает массивы в несколько из четырех байтов, и код не подведет их до тех пор, пока вы не напишете еще три символа. Даже тот же самый компилятор может распределять переменные в кадре стека по-разному, чтобы гарантировать, что выравнивание выполнено.
Это то, что они подразумевают под неопределенным: вы не знаете что произойдет.
Только не делайте этого. – ojblass
@ojblass: Не могу согласиться больше. @ Alien01: Вы отметили этот вопрос C++, так почему бы вам не использовать std :: string? – dalle
Хорошо, я поместил этот трехстрочный код в одну функцию, и я вызываю эту функцию из main. Это единственная функция, которая вызывается из основного и ничего больше в программе. Мое единственное намерение - найти, почему он работает на 12 символов и не работает с 13. – anand