Зачем использовать bzero над memset?

В классе системного программирования я взял этот предыдущий семестр, нам пришлось реализовать базовый клиент/сервер в C. При инициализации структур, например буферов sock_addr_in или char (которые мы использовали для отправки данных назад и между клиентом и сервером) профессор поручил нам использовать bzero, а не memset для их инициализации. Он никогда не объяснял, почему, и мне любопытно, есть ли веская причина для этого?

Я вижу здесь: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown, что bzero более эффективен из-за того, что только когда-либо будет обнулять память, поэтому нет необходимости выполнять дополнительную проверку, которую может выполнять memset. Это все еще не обязательно является повод для абсолютно не использовать memset для обнуления памяти.

bzero считается устаревшим и, кроме того, не является стандартной функцией С. Согласно руководству, memset является предпочтительным по сравнению с bzero по этой причине. Итак, почему вы хотите использовать bzero над memset? Просто для повышения эффективности, или это нечто большее? Кроме того, каковы преимущества memset over bzero, которые делают его де-факто предпочтительным вариантом для более новых программ?

Ответ 1

Я не вижу причин предпочитать bzero над memset.

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

Теперь, относительно эффективности, компиляторы вроде gcc используют встроенные реализации для memset, которые переключаются на конкретную реализацию при обнаружении константы 0. То же самое для glibc, когда встроенные функции отключены.

Ответ 2

Я предполагаю, что вы использовали (или ваш учитель был под влиянием) UNIX Network Programming от W. Richard Stevens. Он часто использует bzero вместо memset, даже в самой последней версии. Книга настолько популярна, я думаю, что она стала идиомой в сетевом программировании, поэтому вы все еще видите ее.

Я бы придерживался memset просто потому, что bzero устарел и снижает переносимость. Я сомневаюсь, что вы увидите какие-либо реальные выгоды от использования одного над другим.

Ответ 3

Единственное преимущество, которое, по моему мнению, bzero() превышает memset() для установки нулевой памяти, - это вероятность того, что ошибка будет сделана.

Не раз я сталкивался с ошибкой, которая выглядела так:

memset(someobject, size_of_object, 0);    // clear object

Компилятор не будет жаловаться (хотя, возможно, на некоторых компиляторах могут появиться некоторые уровни предупреждений), и эффект будет заключаться в том, что память не очищается. Поскольку это не уничтожает объект - он просто оставляет его в покое - есть приличный шанс, что ошибка может не проявиться в чем-то очевидном.

Тот факт, что bzero() не является стандартным, является незначительным раздражителем. (FWIW, я бы не удивился, если большинство вызовов функций в моих программах нестандартны, ведь написание таких функций - это моя работа).

В комментарии к другому ответу здесь Аарон Ньютон привел следующее из Unix Network Programming, том 1, 3-е издание, Стивенс и др., раздел 1.2 (выделено мной):

bzero не является функцией ANSI C. Это происходит от раннего Беркели сетевой код. Тем не менее, мы используем его во всем тексте, вместо этого функции ANSI C memset, потому что bzero легче запомнить (только с двумя аргументами), чем memset (с тремя аргументами). Почти каждый поставщик, поддерживающий API сокетов, также предоставляет bzero и если нет, мы предоставляем определение макроса в нашем заголовке unp.h.

Действительно, автор TCPv3 [TCP/IP Illustrated, Volume 3 - Stevens 1996] допустил ошибку при замене второго и третий аргумент memset в 10 случаях в первом печать. Компилятор C не может поймать эту ошибку, поскольку оба аргумента имеют один и тот же тип. (На самом деле второй аргумент равен int и третий аргумент size_t, который обычно равен unsigned int, но указанные значения, 0 и 16, соответственно, все еще приемлемы для другого типа аргумента.) Вызов memset продолжал работать, потому что только некоторые из функций сокета фактически требуют, чтобы окончательные 8 байтов структуры адресов интернет-сокета должны быть установлены в 0. Тем не менее, это была ошибка, и этого можно было бы избежать, используя bzero, потому что замена двух аргументов на bzero всегда будет захваченный компилятором C, если используются прототипы функций.

Я также считаю, что подавляющее большинство вызовов memset() равны нулю в памяти, поэтому почему бы не использовать API, который подходит для этого случая использования?

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

Ответ 5

Вероятно, вы не должны использовать bzero, это не стандартный C, это была вещь POSIX.

И обратите внимание, что слово "было" - оно устарело в POSIX.1-2001 и удалено в POSIX.1-2008 в знак уважения к memset, поэтому вам лучше использовать стандартную функцию C.

Ответ 6

Хотелось бы упомянуть что-то о аргументе 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 или любом числе интерфейса kernel/syscall, мне не о чем беспокоиться. Все, о чем я должен беспокоиться, это то, что вызов удовлетворяет требованию нулевого буфера. Другие упоминали о том, какой из них предпочтительнее другого, поэтому я остановлюсь здесь.

Ответ 7

Иметь это так, как вам нравится.: -)

#ifndef bzero
#define bzero(d,n) memset((d),0,(n))
#endif

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

  • Оригинал bzero ничего не возвращает, memset возвращает указатель void (d). Это можно устранить, добавив в определение тип void.
  • #ifndef bzero не мешает вам скрывать исходную функцию, даже если она существует. Он проверяет существование макроса. Это может вызвать много путаницы.
  • Невозможно создать указатель на макрос. При использовании bzero с помощью указателей функций это не сработает.

Ответ 8

Для функции 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, если используются прототипы функций.

Ответ 9

memset принимает 3 параметра, bzero занимает 2 в ограниченном объеме памяти, что дополнительный параметр будет занимать еще 4 байта, и большую часть времени он будет использоваться для установки всего на 0