Зачем использовать 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, которые делают его де-факто предпочтительным вариантом для новых программ?


person PseudoPsyche    schedule 13.06.2013    source источник
comment
кажется, что memset более портативен   -  person tay10r    schedule 14.06.2013
comment
Зачем использовать bzero вместо memset? - Не надо. Memset стандартен, bzero - нет.   -  person    schedule 14.06.2013
comment
у меня вопрос - почему бы в этом случае не использовать calloc?   -  person EkoostikMartin    schedule 14.06.2013
comment
bzero - это BSDism (). memset () - это ansi-c. в настоящее время bzero (), вероятно, будет реализован как макрос. Попросите профессора побриться и почитать книги. эффективность - выдуманный аргумент. Системный вызов или переключение контекста могут легко стоить десятки тысяч тактов, один проход через буфер выполняется на скорости шины. Если вы хотите оптимизировать сетевые программы: минимизируйте количество системных вызовов (путем чтения / записи больших блоков)   -  person wildplasser    schedule 14.06.2013
comment
Идея о том, что memset может быть немного менее эффективным из-за того, что выполняется немного больше проверок, определенно является случаем преждевременной оптимизации: какие бы выгоды вы ни увидели от пропуска одной или двух инструкций ЦП, они не стоят того, когда вы можете поставить под угрозу переносимость своего кода. . bzero устарело, и это достаточная причина не использовать его.   -  person Sergey Kalinichenko    schedule 14.06.2013
comment
Связано: bzero () и bcopy () по сравнению с memset () и memcpy ()   -  person Palec    schedule 11.01.2015
comment
Часто вместо этого вы можете добавить инициализатор `= {0}` и вообще не вызывать функцию. Это стало проще, когда примерно на рубеже веков C перестал требовать предварительного объявления локальных переменных. Однако некоторая действительно старая бумажная посуда все еще застряла в прошлом веке.   -  person MSalters    schedule 12.10.2015
comment
Ваш профессор когда-нибудь приводил вам причину?   -  person S.S. Anne    schedule 15.04.2020
comment
@SSAnne нет, но, скорее всего, это произошло из книги, рекомендованной для курса, на который он повлиял, как указано в одном из ответов ниже: stackoverflow.com/a/17097072/1428743   -  person PseudoPsyche    schedule 17.04.2020


Ответы (9)


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

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

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

person ouah    schedule 13.06.2013
comment
Спасибо. Это имеет смысл. Я был почти уверен, что в этом случае всегда следует использовать memset, но был сбит с толку, почему мы его не использовали. Спасибо за разъяснение и подтверждение моих мыслей. - person PseudoPsyche; 14.06.2013
comment
У меня было много проблем с bzero неработающими реализациями. На невыровненных массивах он использовался для превышения заданной длины и обнуления немного большего количества байтов. Ни разу не было такой проблемы после перехода на memset. - person rustyx; 21.08.2014
comment
Не забывайте о memset_s, который следует использовать, если вы хотите, чтобы компилятор не оптимизировал незаметно, отключив вызов очистки памяти для какой-либо цели, связанной с безопасностью (например, отключение области памяти, которая содержала конфиденциальную часть информации, такой как пароль в открытом виде). - person Christopher Schultz; 13.01.2018

Полагаю, вы использовали (или на вашего учителя повлияло) Сетевое программирование UNIX У. Ричарда Стивенса. Он часто использует bzero вместо memset, даже в самой последней редакции. Книга настолько популярна, что я думаю, она стала идиомой в сетевом программировании, поэтому вы до сих пор видите ее использование.

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

person austin    schedule 13.06.2013
comment
Вы были бы правы. У нас не было необходимых учебников для этого курса, но я только что проверил программу еще раз, и Сетевое программирование UNIX действительно указано как дополнительный ресурс. Спасибо. - person PseudoPsyche; 14.06.2013
comment
На самом деле все еще хуже. Он объявлен устаревшим в POSIX.1-2001 и удален в POSIX.1-2008. - person paxdiablo; 20.08.2013
comment
Цитируя страницу 8 третьего издания книги Сетевое программирование UNIX У. Ричарда Стивенса - Действительно, автор TCPv3 совершил ошибку, заменив второй и третий аргументы на memset в 10 случаях, когда первая печать. Компилятор AC не может отловить эту ошибку, потому что оба вхождения совпадают ... это была ошибка, и ее можно было избежать с помощью bzero, потому что замена двух аргументов на bzero всегда будет перехвачена компилятором C, если используются прототипы функций. Однако, как указал paxdiablo, bzero устарел. - person Aaron Newton; 25.07.2015
comment
@AaronNewton, вы должны добавить это к ответу Майкла, поскольку он подтверждает то, что он сказал. - person Synetech; 11.09.2015

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

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

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

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

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

В комментарии к другому ответу здесь Аарон Ньютон процитировал следующее из Unix Network Programming, Volume 1, 3rd Edition, Stevens, et al., Section 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().

person Michael Burr    schedule 13.06.2013
comment
Да, я полагаю, это могло быть причиной при работе в такой обстановке в классе, чтобы сделать это потенциально менее запутанным для учеников. Однако я не думаю, что это было в случае с моим профессором. Он был очень большим учителем RTFM. Если у вас был вопрос, на который можно было бы ответить в руководстве, он открывал страницы руководства на проекторе в классе и показывал вам. Он очень хотел внушить всем, что руководство предназначено для чтения и отвечает на большинство ваших вопросов. Я благодарен за это, в отличие от некоторых других профессоров. - person PseudoPsyche; 14.06.2013
comment
Я думаю, что это аргумент, который можно привести даже вне класса - я видел эту ошибку в производственном коде. Мне кажется, что это простая ошибка. Я также предполагаю, что подавляющее большинство вызовов memset() просто обнуляют блок памяти, что, на мой взгляд, является еще одним аргументом в пользу bzero(). Что вообще означает буква «b» в bzero()? - person Michael Burr; 14.06.2013
comment
+1. Это memset нарушает общий порядок параметров буфера, buffer_size делает его особенно подверженным ошибкам IMO. - person jamesdlin; 20.08.2013
comment
В Паскале этого избегают, называя это fillchar, и он принимает char. Большинство компиляторов C / C ++ выберут это. Это заставляет меня задаться вопросом, почему компиляторы не говорят, что вы передаете 32- или 64-битный указатель, где ожидается байт, и твердо пинают вас в ошибках компилятора. - person Móż; 30.10.2013
comment
Я предполагаю, что b в bzero происходит от «буфера», @MichaelBurr. Но он также может быть «двоичным» или «байтовым». - person Palec; 29.04.2015
comment
Похоже, что это действительно правильный ответ на вопрос. - person Synetech; 11.09.2015
comment
@MichaelBurr, что это за ошибка в приведенном вами примере memset ..? не могли бы вы это объяснить? - person Gewure; 23.03.2017
comment
@Gewure второй и третий аргумент расположены в неправильном порядке; указанный в кавычках вызов функции ничего не делает - person Ichthyo; 20.04.2018

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

person gumchew    schedule 22.04.2015
comment
Это происходит потому, что некоторые версии GCC выдают код для memset(ptr, 0, n), когда они видят bzero(ptr, n), и они не могут преобразовать его во встроенный код. - person zwol; 23.05.2018
comment
@zwol На самом деле это макрос. - person S.S. Anne; 15.04.2020
comment
@ S.S.Anne gcc 9.3 на моем компьютере выполняет это преобразование самостоятельно, без помощи макросов в системных заголовках. extern void bzero(void *, size_t); void clear(void *p, size_t n) { bzero(p, n); } вызывает вызов memset. (Включите stddef.h вместо size_t, чтобы ничего не мешало.) - person zwol; 15.04.2020

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

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

person paxdiablo    schedule 20.08.2013
comment
Что вы имеете в виду под стандартным C? Вы имеете в виду, что его нет в стандартной библиотеке C? - person Koray Tugay; 28.04.2015
comment
@Koray, стандарт C означает стандарт ISO и, да, bzero не является его частью. - person paxdiablo; 28.04.2015
comment
Нет, я имею в виду, я не понимаю, что вы имеете в виду под каким-либо стандартом. Под стандартом ISO подразумевается стандартная библиотека C? Что идет с языком? Минимальная библиотека, которая, как мы знаем, будет там? - person Koray Tugay; 28.04.2015
comment
@Koray, ISO - это организация по стандартизации, которая отвечает за стандарт C, текущий - C11, а более ранние - C99 и C89. Они устанавливают правила, которым должна следовать реализация, чтобы считаться C. Итак, да, если в стандарте говорится, что реализация должна предоставлять memset, она будет там для вас. В противном случае это не C. - person paxdiablo; 29.04.2015

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

person havish    schedule 23.12.2013
comment
Такую ошибку также можно избежать с помощью memset, если вы просто думаете о вызове как о настройке этой памяти на это значение для этого размера, или если у вас есть IDE, которая дает вам прототип, или даже если вы просто знаете, что делаете: -) - person paxdiablo; 29.04.2015
comment
Согласитесь, но эта функция создавалась в то время, когда такие интеллектуальные IDE не были доступны для поддержки. - person havish; 25.07.2015

Вкратце: memset требуется больше операций сборки, чем bzero.

Это источник: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown

person Tal Bar    schedule 16.01.2014
comment
Да, это то, о чем я упоминал в ОП. Я даже ссылался на эту страницу. Оказывается, это не имеет большого значения из-за некоторых оптимизаций компилятора. Подробнее см. В принятом ответе ouah. - person PseudoPsyche; 18.01.2014
comment
Это только показывает, что одна дрянная реализация memset работает медленно. В MacOS X и некоторых других системах memset использует код, который настраивается во время загрузки в зависимости от используемого процессора, полностью использует векторные регистры, а для больших размеров он использует инструкции предварительной выборки, чтобы получить последний бит. скорости. - person gnasher729; 30.04.2015
comment
меньшее количество инструкций не означает более быстрое выполнение. На самом деле оптимизации часто увеличивают двоичный размер и количество инструкций из-за развертывания цикла, встраивания функций, выравнивания цикла ... Посмотрите на любой приличный оптимизированный код, и вы увидите, что он часто содержит гораздо больше инструкций, чем дерьмовые реализации - person phuclv; 22.11.2019

Будь как хочешь. :-)

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

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

  1. Исходный bzero ничего не возвращает, memset возвращает пустой указатель (d). Это можно исправить, добавив приведение типа к void в определении.
  2. #ifndef bzero не мешает вам скрыть исходную функцию, даже если она существует. Он проверяет наличие макроса. Это может вызвать путаницу.
  3. Невозможно создать указатель функции на макрос. При использовании bzero через указатели функций это не сработает.
person Bruce    schedule 20.03.2014
comment
В чем проблема, @Leeor? Общая антипатия к макросам? Или вам не нравится то, что этот макрос можно спутать с функцией (а, возможно, даже скрыть)? - person Palec; 29.04.2015
comment
@ Палек, последний. Скрытие переопределения как макроса может привести к большой путанице. Другой программист, использующий этот код, думает, что использует одно, и по незнанию вынужден использовать другое. Это бомба замедленного действия. - person Leeor; 29.04.2015
comment
Поразмыслив еще раз, я согласен, что это действительно плохое решение. Среди прочего я нашел техническую причину: при использовании bzero через указатели функций это не сработает. - person Palec; 30.04.2015
comment
Вам действительно следовало назвать свой макрос не bzero. Это зверство. - person Dan Bechard; 02.04.2018

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

person Skynight    schedule 23.07.2019