C11 Приложение K: "объекты, которые перекрываются"
Есть фраза, которая продолжает появляться в Приложении K стандарта C (интерфейсы проверки границ):
.... копирование не должно происходить между объектами, которые перекрываются.
Учитывая, например, strcpy_s( char * restrict s1, rsize_t s1max, char const * restrict s2 )
, в котором s1max
указывает максимальную вместимость s1
включить проверку границ.
Каким именно будет "объект" s1
в этот момент, который не должен пересекаться с "объектом" s2
?
Это будет...
- s1 [0].. s1 [s1max] (до конца буфера, то есть объекта памяти),
или же
- s1[0]..s1[strnlen( s1, s1max)] (до конца строки, то есть объекта строки)?
Если это первое, меня интересует отсутствие согласованности, так как я не знаю размер буфера s2 и должен был бы применить другое определение "объекта".
Если это последнее, мне интересно, не нарушает ли это "обещание", которое дано, поскольку исходная строка и конечная (конечная) строка назначения могут перекрываться, если исходная строка длиннее исходной.
Каково намерение / предполагаемое определение "объекта" здесь?
2 ответа
Я считаю, что намерение таково, что s1max
символы начиная с s1
не должен накладываться ни на один из символов в s2
в том числе нулевой терминатор. K.3.7.1.3p5 говорит, что:
- Все элементы, следующие за завершающим нулевым символом (если есть), записаны
strcpy_s
в массиве символов s1max, на которые указываетs1
принимать неопределенные значения, когдаstrcpy_s
возвращается. [418]
со сноской 418 о том, что
- Это позволяет реализации копировать символы из
s2
вs1
одновременно проверяя, являются ли какие-либо из этих символов нулевыми. Такой подход может написать символ для каждого элементаs1
перед обнаружением, что первый элемент должен быть установлен в нулевой символ.
Тем не менее, Microsoft говорит, что "если source
а также dest
перекрытие, поведение не определено ", так что это намекает на то, что на самом деле все может произойти в этом случае. Это, кажется, сводит на нет полезность интерфейса проверки границ.
Это встречается повсеместно в стандарте, не только в дополнительном интерфейсе проверки границ, но и в обязательных библиотечных функциях, таких как strcpy
, Интерфейсные функции проверки границ просто унаследовали один и тот же текст.
Формальное определение объекта:
3.15
объект
область хранения данных в среде исполнения, содержимое которой может представлять значения
Исходя из этого, строка должна быть целым массивом, включая нулевой терминатор. Потому что такая функция, как strcpy
сломался бы, если нулевой терминатор был каким-то образом перезаписан во время копирования - он должен рассматриваться как часть объекта (массива).
Кажется, что нет определения термина "перекрытие", но цель довольно ясна: предотвратить такие ситуации:
char str[] = "foobar";
strcpy(str+3,str);
где одна возможная реализация strcpy
было бы while(*dst++ = *src++){}
, Который сломался бы, поскольку он никогда не достигнет нулевого терминатора, и мы закончили бы писать вне границ.
Примечательно, что вы уже обещали компилятору, что параметры не перекрываются при передаче их ожидающей функции restrict
указатели. Текст в стандарте, касающийся того, что перекрытия не определены, еще яснее.
в strcpy
Например, любой доступ к чему-либо dst
указывает на, не разрешается изменять то, что str
указывает на, или мы нарушаем определение restrict
(C17 6.7.3) и тем самым вызывает неопределенное поведение.
Насколько я знаю, это всегда ответственность программиста. Ни один из известных мне компиляторов не выдает диагностические сообщения restrict
нарушения на стороне звонящего.