Навіщо використовувати bzero над мемсетом?


156

У класі системного програмування, який я взяв у цьому попередньому семестрі, нам довелося реалізувати базовий клієнт / сервер у 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над тим, bzeroщо роблять його фактично кращим варіантом для нових програм?


28
"Навіщо використовувати bzero над мемсетом?" - Не варто. Memset стандартний, bzero - ні.

30
bzero - це BSDism (). memset () - ansi-c. нині bzero (), ймовірно, буде реалізований як макрос. Попросіть свого професора поголитись і прочитати деякі книги. ефективність - хибний аргумент. Системний виклик або контекстний комутатор може легко коштувати десятки тисяч тактових годин, один прохід по буфері працює зі швидкістю шини. Якщо ви хочете оптимізувати мережеві програми: мінімізуйте кількість системних дзвінків (читаючи / записуючи більші шматки)
wildplasser

7
Ідея, яка memsetможе бути дещо менш ефективною через "трохи більше перевірки", безумовно, є випадком передчасної оптимізації: незалежно від того, що ви можете отримати від пропуску інструкції процесора чи двох, не варто, коли ви можете поставити під загрозу переносимість вашої код. bzeroє застарілим, і це є достатньою підставою не використовувати його.
dasblinkenlight

4
Часто ви можете замість цього додати ініціалізатор `= {0}` і взагалі не викликати функцію. Це стало простіше, коли на межі століття С перестала вимагати попереднього оголошення локальних змінних. Деякі справді старі вироби з паперу все ще застрягли в попередньому столітті.
MSalters

1
@SSAnne немає, але це , швидше за все , виникло з рекомендованих книг для курсу він перебував під впливом, як вже згадувалися в одному з відповідей нижче: stackoverflow.com/a/17097072/1428743
PseudoPsyche

Відповіді:


152

Я не бачу причин віддавати перевагу bzeroнад memset.

memsetє стандартною функцією C, хоча bzeroніколи не була стандартною функцією C. Обґрунтування, мабуть, тому, що ви можете досягти точно такої ж функції, використовуючи memsetфункцію.

Тепер щодо ефективності компілятори gccвикористовують вбудовані реалізації, для memsetяких переходять на певну реалізацію, коли 0виявляється константа . Те ж саме, glibcколи вбудовані файли вимкнено.


Дякую. Це має сенс. Я був майже впевнений, що memsetзавжди слід використовувати в цьому випадку, але був розгублений, чому ми його не використовували. Дякую за уточнення та підтвердження моїх думок.
PseudoPsyche

1
У мене було багато проблем зі зламаними bzeroреалізаціями. На нерівневих масивах він використовував для перекреслення заданої довжини і нульового значення трохи більше байтів. Ніколи не виникало такого питання після переходу на memset.
rustyx

Не забувайте про те, memset_sщо слід використовувати, якщо ви хочете переконатися, що компілятор не спокійно оптимізує виклик "очистити" пам'ять для певних цілей, пов'язаних із безпекою (наприклад, вимивання області пам'яті, яка містила чутливий інформацію, наприклад пароль явного тексту).
Крістофер Шульц

69

Я здогадуюсь, що ви використовували (або на вашого вчителя вплинуло) мережеве програмування UNIX W. Річард Стівенс. Він використовує bzeroчасто замість memset, навіть у найсучаснішому виданні. Книга настільки популярна, я думаю, що вона стала ідіомою в мережевому програмуванні, тому ви все ще бачите її використаною.

Я б дотримувався цього memsetпросто тому bzero, що застарів і зменшує мобільність. Сумніваюсь, ви побачили б реальні вигоди від використання одного над іншим.


4
Ви були б правильні. У нас не було потрібних підручників для цього курсу, але я просто перевірив програму ще раз і мережеве програмування UNIX дійсно вказане як необов'язковий ресурс. Дякую.
PseudoPsyche

9
Це насправді гірше за це. Він був застарілий у POSIX.1-2001 та видалений у POSIX.1-2008.
paxdiablo

9
Цитуючи сторінку 8 третього видання мережевого програмування UNIX У. Річарда Стівенса - Дійсно, автор TCPv3 допустив помилку підмінивши другий та третій аргументи на запам'ятовування у 10 явищах першого друку. Компілятор змінного струму не може впізнати цю помилку, оскільки обидва випадки однакові ... це була помилка, і її можна було б уникнути, використовуючи bzero, тому що заміна двох аргументів на bzero завжди буде спіймана компілятором C, якщо використовуються прототипи функцій. Однак, як вказував paxdiablo, bzero є застарілим.
Аарон Ньютон

@AaronNewton, ви повинні додати це до відповіді Майкла, оскільки це підтверджує те, що він сказав.
Synetech

52

Одна перевага, яку я думаю, bzero()має надmemset() встановлення пам'яті на нуль, є те, що знижується ймовірність помилки.

Не раз я стикався з помилкою, яка виглядала так:

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

Компілятор не поскаржиться (хоча, можливо, на деяких компіляторах може виникнути деякий рівень попередження), і наслідком буде те, що пам'ять не очищена. Оскільки це не сміття об'єкта - він просто залишає його в спокої - є пристойний шанс, що помилка може не виявитися в чомусь очевидному.

Справа в тому, що bzero() не є стандартним, є незначним подразником. (FWIW, я б не здивувався, якщо більшість викликів функцій у моїх програмах є нестандартними; адже написання таких функцій - це моя робота).

У коментарі до іншої відповіді тут Аарон Ньютон цитував наступне з Мережевого програмування Unix, Том 1, 3-е видання Стівенса та ін., Розділ 1.2 (наголос додано):

bzeroне є функцією ANSI C. Він отриманий з раннього коду мереж Berkely. Тим не менш, ми використовуємо його у всьому тексті замість функції ANSI C memset, оскільки bzeroйого легше запам'ятати (лише з двома аргументами), ніж memset(з трьома аргументами). Практично кожен постачальник, який підтримує API сокетів, також надає bzero, і якщо ні, то ми надаємо визначення макросу в нашому unp.hзаголовку.

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

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

Можливим недоліком bzero()є те, що компілятори можуть скоріше оптимізувати, memcpy()оскільки це стандарт, і тому вони можуть бути написані для його розпізнавання. Однак майте на увазі, що правильний код все-таки краще, ніж неправильний код, оптимізований. У більшості випадків використання bzero()не спричинить помітного впливу на продуктивність вашої програми, і це bzero()може бути функція макро- чи вбудованого розширення memcpy().


Так, я вважаю, що це може бути міркуванням під час роботи в такому класі, щоб зробити його менш заплутаним для учнів. Однак я не думаю, що це було з моїм професором. Він був дуже великим вчителем RTFM. Якщо у вас виникло запитання, на яке можна було б відповісти в посібнику, він перетягнув бідні сторінки на проекторі в класі і показав вам. Він дуже сподівався на те, щоб все в думках було все, що посібник є для читання і відповідає на більшість ваших запитань. Я вдячний за це, на відміну від деяких інших професорів.
PseudoPsyche

5
Я думаю, що це аргумент, який можна висловити навіть поза аудиторією - я бачив цю помилку у виробничому коді. Це вражає мене легкою помилкою. Я б також здогадався, що переважна більшість memset()дзвінків - це просто нульовий блок пам'яті, що, на мою думку, є ще одним аргументом bzero(). Що означає 'b' bzero()взагалі?
Майкл Берр

7
+1. Це memsetпорушує загальний порядок впорядкування параметрів "buffer, buffer_size", робить його особливо схильним до помилок IMO.
jamesdlin

У Паскалі вони уникають цього, називаючи його "наповнювачем", і він приймає знак. Більшість компіляторів C / C ++ вибирають це. Що змушує мене замислитись, чому компілятори не кажуть "ти передаєш 32/64 бітовий покажчик, де очікується байт", і натискають тебе твердо на помилки компілятора.
Móż

1
@ Другий та третій аргументи Gewure в неправильному порядку; цитований функціональний дзвінок точно нічого
Іхтьо

4

Хотілося згадати щось про аргумент bzero vs. 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 або будь-якій кількості інтерфейсу ядро ​​/ syscall, я не повинен про них турбуватися. Все, про що я повинен хвилюватися, - це те, що виклик задовольняє вимогу нульового буфера. Інші згадували про те, який з них є кращим над іншим, тому я зупинюсь тут.


Це трапляється тому, що деякі версії GCC передаватимуть код, memset(ptr, 0, n)коли вони бачать, bzero(ptr, n)і вони не можуть перетворити його в вбудований код.
zwol

@zwol Це насправді макрос.
СС Енн

1
@SSAnne gcc 9.3 на моєму комп’ютері робить це перетворення самостійно, без допомоги макросів у заголовках системи. extern void bzero(void *, size_t); void clear(void *p, size_t n) { bzero(p, n); }виробляє дзвінок на memset. (Включайте stddef.hдля size_tбез іншого, що може заважати.)
zwol

4

Напевно, ви не повинні використовувати bzero, це насправді не стандартний C, це була річ POSIX.

І зауважте, що слово "було" - воно було застаріле в POSIX.1-2001 та видалене в POSIX.1-2008 з повагою до мемсету, тому вам краще використовувати стандартну функцію C.


Що ви маєте на увазі під стандартом C? Ви маєте на увазі, що його немає в стандартній бібліотеці С?
Корай Тугай

@Koray, стандарт C означає стандарт ISO і, так, bzeroне є частиною цього.
paxdiablo

Ні, я маю на увазі, я не знаю, що ви маєте на увазі під будь-яким стандартом. Чи означає стандарт ISO стандартну бібліотеку С? Що йде з мовою? Мінімальна бібліотека, яку ми знаємо, буде там?
Корай Тугай

2
@Koray, ISO - це організація стандартів, яка відповідає за стандарт C, поточний - C11, а більш ранні - C99 і C89. Вони встановлюють правила, яких слід дотримуватися для того, щоб їх вважати C. Так, якщо стандарт каже, що імплементація повинна містити мемсети, це буде для вас. Інакше це не C.
paxdiablo

2

Для функції 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, якщо будуть використані прототипи функцій.


1
Такої помилки можна також уникнути за допомогою мемсету, якщо ви просто думаєте про виклик як "встановити цю пам'ять на це значення для цього розміру", або якщо у вас є IDE, який дає вам прототип або навіть якщо ви просто знаєте, що ви роби :-)
paxdiablo

Погодьтеся, але ця функція була створена в той час, коли такі інтелектуальні ІДЕ не були доступні для підтримки.
буйний

2

Якщо коротко: тоді memset потрібно більше операцій по складанню bzero.

Це джерело: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown


Так, це одне, що я згадував в ОП. Я фактично навіть пов’язаний з цією точною сторінкою. Виявляється, що, здається, насправді не має великого значення через деякі оптимізації компілятора. Більш детально див. Прийняту відповідь від ouah.
ПсевдоПсихея

6
Це свідчить лише про те, що реалізація мемсету на одному смітті відбувається повільно. У MacOS X та деяких інших системах memset використовує код, який встановлюється під час завантаження залежно від процесора, який ви використовуєте, повною мірою використовує векторні регістри, а для великих розмірів використовує інструкції з попереднього вибору розумними способами отримати останній біт швидкості.
gnasher729

менша кількість інструкцій не означає швидше виконання. Насправді оптимізація часто збільшує двійковий розмір та кількість інструкцій через розгортання циклу, вбудовування функцій, вирівнювання циклу ... Подивіться на будь-який гідний оптимізований код, і ви побачите, що в ньому часто є набагато більше вказівок, ніж у
лайнових

2

Майте все, як вам подобається. :-)

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

Зауважте, що:

  1. Оригінал bzeroнічого не повертає, memsetповертає недійсний покажчик ( d). Це можна виправити, додавши в визначення визначення typecast для недійсності.
  2. #ifndef bzeroне заважає вам приховати оригінальну функцію, навіть якщо вона існує. Він перевіряє існування макросу. Це може призвести до великої плутанини.
  3. Неможливо створити покажчик функції на макрос. При використанні bzeroфункціональних покажчиків це не працюватиме.

1
У чому проблема в цьому, @Leeor? Загальна антипатія до макросів? Або вам не подобається той факт, що цей макрос можна плутати з функцією (а можливо, навіть приховувати)?
Палець

1
@Palec, остання. Приховування переосмислення як макросу може призвести до стільки плутанини. Інший програміст, який використовує цей код, вважає, що він використовує одне, і несвідомо змушений використовувати інше. Це бомба за часом.
Ліор

1
Давши йому ще одну думку, я погоджуюся, що це дійсно погане рішення. Серед іншого я знайшов технічну причину: якщо використовувати bzeroчерез функціональні вказівники, це не вийде.
Палець

Ви дійсно повинні були назвати свій макрос чимось іншим, ніж bzero. Це жорстокість.
Ден Бешард

-2

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.