Зачем использовать bzero над memset?
В классе системного программирования, который я взял в предыдущем семестре, нам пришлось реализовать базовый клиент / сервер на C. При инициализации структур, например sock_addr_in
или символьные буферы (которые мы использовали для отправки данных назад и вперед между клиентом и сервером), профессор поручил нам использовать только bzero
и не memset
инициализировать их. Он никогда не объяснял почему, и мне любопытно, есть ли на то веские причины для этого?
Я вижу здесь: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown что bzero
является более эффективным из-за того факта, что когда-либо будет только обнуление памяти, поэтому он не должен делать никакой дополнительной проверки, что memset
может сделать. Это по-прежнему не обязательно является причиной абсолютно не использовать memset
для обнуления памяти, хотя.
bzero
считается устаревшим и, кроме того, не является стандартной функцией C. Согласно инструкции, memset
предпочтительнее bzero
по этой причине. Так почему вы хотите все еще использовать bzero
над memset
? Просто для повышения эффективности или это нечто большее? Аналогично, каковы преимущества memset
над bzero
что делает его де-факто предпочтительным вариантом для новых программ?
8 ответов
Я не вижу причин предпочитать bzero
над memset
,
memset
стандартная функция C в то время как bzero
никогда не был стандартной функцией C. Обоснование, вероятно, потому что вы можете достичь точно такой же функциональности, используя memset
функция.
Что касается эффективности, компиляторы, как gcc
использовать встроенные реализации для memset
которые переключаются на конкретную реализацию, когда постоянная 0
обнаружен. То же самое для glibc
когда встроенные функции отключены.
Я предполагаю, что вы использовали (или под влиянием вашего учителя) сетевое программирование UNIX У. Ричарда Стивенса. Он использует bzero
часто вместо memset
даже в самом современном издании. Книга настолько популярна, что я думаю, что она стала идиомой в сетевом программировании, поэтому вы до сих пор ее используете.
Я бы придерживался memset
просто потому что bzero
устарела и снижает переносимость. Я сомневаюсь, что вы увидите какие-либо реальные выгоды от использования одного над другим.
Единственное преимущество, которое я считаю bzero()
имеет более memset()
для установки памяти в ноль есть вероятность того, что ошибка будет сделана.
Я не раз сталкивался с ошибкой, которая выглядела так:
memset(someobject, size_of_object, 0); // clear object
Компилятор не будет жаловаться (хотя, возможно, на некоторых компиляторах могут появиться некоторые уровни предупреждений), и в результате память не очищается. Поскольку это не разрушает объект, а просто оставляет его в покое, есть большая вероятность, что ошибка может не проявиться ни в чем очевидном.
Дело в том, что bzero()
не является стандартным второстепенным раздражителем. (FWIW, я не удивлюсь, если большинство вызовов функций в моих программах будут нестандартными; на самом деле написание таких функций является моей работой).
В комментарии к другому ответу здесь Аарон Ньютон процитировал следующее из Сетевого программирования Unix, Том 1, 3 -е издание, Стивенс и др., Раздел 1.2 (выделение добавлено):
bzero
не является функцией ANSI C. Он получен из раннего сетевого кода Беркли. Тем не менее, мы используем его по всему тексту, а не ANSI Cmemset
функция, потому чтоbzero
легче запомнить (только с двумя аргументами), чемmemset
(с тремя аргументами). Почти каждый поставщик, который поддерживает сокеты API, также предоставляетbzero
и, если нет, мы даем определение макроса в нашемunp.h
заголовок.Действительно, автор TCPv3 [Иллюстрированный TCP/IP, том 3 - Стивенс, 1996] совершил ошибку, поменяв местами второй и третий аргументы
memset
в 10 случаях в первой печати. Компилятор A C не может перехватить эту ошибку, потому что оба аргумента имеют одинаковый тип. (На самом деле, второй аргументint
и третий аргументsize_t
, который, как правило,unsigned int
, но указанные значения, 0 и 16, соответственно, все еще приемлемы для другого типа аргумента.) Вызовmemset
все еще работал, потому что только некоторые функции сокетов фактически требуют, чтобы последние 8 байтов структуры адреса интернет-сокета были установлены в 0. Тем не менее, это была ошибка, и ее можно было избежать, используяbzero
потому что поменять два аргументаbzero
всегда будет перехватываться компилятором C, если используются прототипы функций.
Я также считаю, что подавляющее большинство призывов к memset()
к нулю памяти, так почему бы не использовать API, который приспособлен для этого варианта использования?
Возможный недостаток bzero()
является то, что компиляторы могут с большей вероятностью оптимизировать memcpy()
потому что это стандартно, и поэтому они могут быть написаны, чтобы признать это. Однако имейте в виду, что правильный код все же лучше, чем неправильный код, который был оптимизирован. В большинстве случаев, используя bzero()
не окажет заметного влияния на производительность вашей программы, и это bzero()
может быть макросом или встроенной функцией, которая расширяется до memcpy()
,
Имейте это так, как вам нравится.:-)
#ifndef bzero
#define bzero(d,n) memset((d),0,(n))
#endif
Обратите внимание, что:
- Оригинал
bzero
ничего не возвращает,memset
возвращает пустой указатель (d
). Это можно исправить, добавив типовое значение void в определение. #ifndef bzero
не мешает вам скрыть оригинальную функцию, даже если она существует. Он проверяет наличие макроса. Это может вызвать много путаницы.- Невозможно создать указатель на функцию макроса. Когда используешь
bzero
через указатели функций это не будет работать.
Вы, вероятно, не должны использовать bzero
На самом деле это не стандартный C, это была вещь POSIX.
И обратите внимание, что слово "было" - оно устарело в POSIX.1-2001 и удалено в POSIX.1-2008 из-за memset, поэтому вам лучше использовать стандартную функцию C.
Короче: memset
требуется больше сборочных операций, чем bzero
,
Это источник: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown
Для функции memset вторым аргументом является int
и третий аргумент size_t
,
void *memset(void *s, int c, size_t n);
который обычно unsigned int
, но если значения, как, 0 and 16
для второго и третьего аргумента соответственно вводятся в неправильном порядке как 16 и 0, тогда такой вызов memset все еще может работать, но ничего не будет делать. Потому что количество байтов для инициализации указано как 0
,
void bzero(void *s, size_t n)
Такую ошибку можно избежать, используя bzero, потому что замена двух аргументов на bzero всегда будет обнаруживаться компилятором C, если используются прототипы функций.
Хотел бы упомянуть кое-что о аргументе bzero против memset. Установите ltrace и затем сравните, что он делает под капотом. В Linux с libc6 (2.19-0ubuntu6.6) звонки выполняются одинаково (через ltrace ./test123
):
long m[] = {0}; // generates a call to memset(0x7fffefa28238, '\0', 8)
int* p;
bzero(&p, 4); // generates a call to memset(0x7fffefa28230, '\0', 4)
Мне сказали, что если я не работаю в недрах библиотеки libc или какого-либо другого интерфейса ядра / системного вызова, мне не о чем беспокоиться. Все, о чем я должен беспокоиться, это то, что вызов удовлетворяет требованию обнуления буфера. Другие упоминали о том, какой из них предпочтительнее другого, поэтому я на этом остановлюсь.
Memset принимает 3 параметра, bzero занимает 2 в памяти с ограничениями, что дополнительный параметр займет еще 4 байта и большую часть времени он будет использоваться для установки всего в 0