2010-09-01 3 views
10

Ниже приведена самая популярная реализация strcpy в традиционных системах. Почему dest и src не проверяются на NULL в начале? Я слышал однажды, что в прежние времена память была ограничена, поэтому короткий код всегда был предпочтительным. Будете ли вы реализовывать strcpy и другие подобные функции с проверкой указателя NULL в начале сейчас? Почему нет?Почему никакие проверки работоспособности в устаревшем strcpy()

char *strcpy(char *dest, const char *src) 
{ 
    char *save = dest; 
    while(*dest++ = *src++); 
    return save; 
} 
+3

Это может быть безопасно вообще. Но это также означает, что опытные разработчики должны платить за безопасность (что им не нужно), так что неопытные разработчики не подрываются. –

+0

Опытные разработчики должны платить за безопасность (что они не считают нужным) ... (FTFY). –

+1

@Brian Hooper - нет, если вы используете C, вы должны точно знать, что вам нужно. Мой встроенный код никогда не требует «NULL» проверки на 'strcpy', потому что все буферы статически распределяются и используются напрямую. Нет абсолютно никакого способа, чтобы я когда-либо передавал 'NULL''' strcpy'. Так почему я хочу заплатить цену? В этом нет «не думаю, что мне нужно». – detly

ответ

19

NULL плохой указатель, но так (char*)0x1. Должен ли он также проверить это? По моему мнению (я не знаю окончательной причины, почему), проверки работоспособности в такой операции низкого уровня не учитываются. strcpy() настолько фундаментален, что с ним следует обращаться как с инструкцией asm, и при необходимости вы должны сделать свои собственные проверки работоспособности. Просто мои 2 цента :)

+0

Я согласен: низкоуровневые процедуры должны быть реализованы для повышения эффективности, а процедуры высокого уровня должны включать защиту, когда это применимо. –

+2

+1, указав, что 'NULL' является лишь одним из примеров 99,9% пространства указателей, которое также может быть недействительным. –

+1

Что заставляет вас сказать (char *) 0x1 обязательно плохой указатель? В C99 нулевой указатель * является * специальным случаем в том, что он «гарантирует сравнение неравнозначным с указателем на любой объект или функцию». (6.3.2.3). – JeremyP

3

Скорее всего, причина в том, что strcpy не указан для работы с входами NULL (то есть его поведение в этом случае не определено).

Итак, что должен сделать разработчик библиотеки, если передан NULL? Я бы сказал, что самое лучшее, что можно сделать, это запустить приложение. Подумайте об этом так: авария - довольно очевидный признак того, что что-то пошло не так ... молча игнорируя вход NULL, с другой стороны, может замаскировать ошибку, которую будет намного сложнее обнаружить.

+1

№ 'strcpy' на вводе NULL - это неопределенное поведение, которое может привести к сбою, или оно может * тихо сделать правильную вещь *. Вы, конечно, не можете полагаться на ошибку времени выполнения от использования 'strcpy' с NULL. –

+0

Это может сделать, но реальность такова, что это не так. – Puppy

+0

@Philip: Хорошая точка. Я отредактировал ответ, чтобы удалить ошибочное утверждение, что «правильная вещь - это сбой» (но я бы все же утверждал, что это вещь _best_). –

2

Проверка NULL не была реализована, поскольку самые ранние объекты C поддерживали надежные защиты памяти. Когда процесс попытался прочитать или записать в NULL, контроллер памяти будет сигнализировать CPU о попытке доступа к внедиапазонной памяти (нарушение сегментации), и ядро ​​убьет процесс нарушения.

Это был хороший ответ, потому что код, пытающийся прочитать или записать указатель NULL, сломан; единственный ответ - переписать код для проверки возвращаемых значений от malloc(3) и друзей и предпринять корректирующие действия. К тому моменту, когда вы пытаетесь использовать указатели для нераспределенной памяти, уже слишком поздно принимать правильное решение о том, как исправить ситуацию.

11

Весь язык C написан с девизом «Мы будем вести себя правильно, если программист знает, что он делает». Ожидается, что программист узнает, чтобы сделать все проверки, которые ему нужно сделать. Это не просто проверка на NULL, это обеспечение того, чтобы dest указывает на достаточно выделенной памяти для хранения src, это проверка возвращаемого значения fopen, чтобы убедиться, что файл действительно сделал открытый успешно, зная, когда memcpy безопасно и когда memmove требуется, и так далее.

Получение strcpy для проверки NULL не изменит языковой парадигмы. Вам все равно нужно будет убедиться, что dest указывает на достаточное пространство - и это то, что strcpyне может проверить без изменения интерфейса. Вам также необходимо будет убедиться, что src - '\0' - завершено, что опять strcpy не может проверить.

Есть некоторые стандартные функции библиотеки C, которые do проверить для NULL: например, free(NULL) всегда безопасен. Но в целом, C ожидает, что вы узнаете, что делаете.

[C++ обычно избегает библиотеки <cstring> в пользу std::string и друзей.]

+0

+1 для std :: string –

+0

.., который настолько несовместим, кажется, * предназначен для того, чтобы болеть *. – peterchen

15

Нет никаких проверок здравомыслия, поскольку одна из наиболее важных идеологий C заключается в том, что разработчик обеспечивает здравомыслие. Когда вы предполагаете, что разработчик нормальный, вы получаете язык, который можно использовать для чего угодно и где угодно.

Это неявно заявленная цель - вполне возможно, что кто-то придумает реализацию, которую делает, проверьте это и многое другое. Может, они и есть. Но я сомневаюсь, что многие люди, привыкшие к C, будут кричать, чтобы использовать его, так как им нужно было бы поместить чеки в любом случае, если бы у них был шанс, что их код будет перенесен на более обычную реализацию.

+11

* ... разработчик поставляет здравый смысл. * - Мне это нравится;) – caf

0

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

0

Согласно мне, любая функция, которую вы хотите определить, будет иметь предварительное условие и пост-условие. Уход за предусловиями никогда не должен быть частью функции. Следующее является предварительным условием использования strcpy, взятого с man-страницы.

Функция strcpy() копирует строку, на которую указывает src (включая завершающий символ «\ 0») в массив, на который указывает dest. Строки не должны перекрываться, а длина целевой адреса должна быть достаточно большой, чтобы получить копию.

Теперь, если предварительное условие не выполнено, все может быть неопределенным.

Включу ли я проверку NULL в свой strcpy. Я предпочел бы еще один safe_strcpy, обеспечив безопасность приоритета, который я обязательно включил бы NULL-проверки и обработал бы условия переполнения. И, соответственно, мое предварительное условие изменяется.

6
  1. Это, как правило, лучше для библиотеки, чтобы звонящий решить, что он хочет семантику отказа быть. Что бы вы сделали strcpy, если любой аргумент NULL? Безмолвно ничего не делать? Не удалось выполнить assert (что не является вариантом в не-отладочных сборках)?

  2. Легче отказаться, чем отказаться. Тривиально написать собственный обертку вокруг strcpy, который проверяет входные данные и использует их вместо этого. Если, однако, библиотека сделала это сама, у вас не было бы выбора, чтобы не выполнять эти проверки, за исключением повторной реализации strcpy. (Например, вы, возможно, уже знаете, что аргументы, которые вы передаете strcpy, не являются NULL, и это может быть что-то, о чем вас беспокоит, если вы вызываете его в узком цикле или озабочены минимизацией использования энергии.) В общем, лучше ошибиться на стороне предоставления большей свободы (даже если эта свобода несет дополнительную ответственность).

+0

+1 для пользовательской оболочки, которая реализует вашу политику обработки ошибок. (Хотя я, вероятно, не буду обертывать strcpy индивидуально. Я использую класс StrOnBuf, который обертывает процедуры манипуляции с основным символьным буфером и может быть настроен на то, чтобы обрезать молча, обрезать с помощью отладки assert или throw). – peterchen

+0

+1 Точка 1 - ответ на вопрос ОП. –

0

Для этого не существует определенной семантики ошибок. В частности, нет способа вернуть strcpy значение ошибки. C99 просто заявляет:

strcpy функция возвращает значение из s1.

Так что для соответствующей реализации не было бы возможности вернуть информацию о том, что что-то пошло не так. Так зачем беспокоиться об этом.

Все это добровольно, я думаю, поскольку strcpy заменяется большинством компиляторов очень эффективным ассемблером. Проверки ошибок до вызывающего абонента.

+0

Поскольку поведение не определено, если 'NULL' передано,' strcpy' может, возможно, возвращать что-то, отличное от 's1', когда' s1' является 'NULL'. Или он может не вернуться вообще (крушение или бесконечный цикл). –

+0

Что делать, если вы первый, и их попросили разработать strcpy с нуля, а также написать сами стандарты C99. Вы изменили бы его, чтобы вернуть некоторое значение ошибки? – user436748

+0

@ user436748: к сожалению, это чисто гипотетическое, три варианта. Для дизайна как «высокого уровня» я просто потребовал бы вернуть «NULL», если при ошибке, установить «errno» с указанием, а также что исходные данные в этом случае не изменяются. это делается в нескольких других местах, но не здесь для 'strcpy'. Если бы я проектировал его как «низкий уровень», я бы пошел «просто поступаю правильно», но я бы дополнительно потребовал, чтобы он произвел segfault в случае, если один из указателей равен «NULL». Затем вы спросили, для реального C99 вы можете пойти на 'char * strcpy (char s1 [статический 1], char const s2 [статический 1]);' –

Смежные вопросы