Зачем использовать 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 C memset функция, потому что 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

Обратите внимание, что:

  1. Оригинал bzero ничего не возвращает, memset возвращает пустой указатель (d). Это можно исправить, добавив типовое значение void в определение.
  2. #ifndef bzero не мешает вам скрыть оригинальную функцию, даже если она существует. Он проверяет наличие макроса. Это может вызвать много путаницы.
  3. Невозможно создать указатель на функцию макроса. Когда используешь 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

Другие вопросы по тегам