Neden bzero'yu memset üzerinde kullanıyorsunuz?


156

Bir Sistem Programlama dersinde bu önceki dönemi aldım, C'de temel bir istemci / sunucu uygulamak zorunda kaldık sock_addr_in. Profesör ya da char arabellekleri (istemci ve sunucu arasında ileri ve geri veri gönderirken kullandığımız) profesörü bize sadece onları kullanmamızı bzerove memsetbaşlatmamamızı emretti . Nedenini hiç açıklamadı ve bunun geçerli bir nedeni olup olmadığını merak ediyorum?

Ben buraya bakın: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown o bzeronedeniyle sadece hiç bellek sıfırlama olacak gerçeğine daha verimlidir, öyle değil bu yüzden ek kontroller memsetyapmak zorunda. Yine de, memsetbelleği sıfırlamak için kesinlikle kullanmamak için bir neden gibi görünmüyor .

bzerokullanımdan kaldırılmıştır ve ayrıca standart bir C işlevi değildir. El kitabına göre, bu nedenle memsettercih edilir bzero. Peki neden hala kullanmak isteyeyim bzeroüzerinde memset? Sadece verimlilik artışı için mi yoksa daha fazlası mı? Aynı şekilde, faydaları nelerdir memsetüzerinde bzeroo yeni programlar için fiili tercih edilen seçenek yapmak?


28
"Neden bzero'yu memset üzerinde kullanıyorsunuz?" - Yapma. Memset standart, bzero değil.

30
bzero bir BSDizmdir (). memset (), ansi-c'dir. günümüzde, bzero () muhtemelen bir makro olarak uygulanacaktır. Profesörünüzden kendini tıraş etmesini ve bazı kitaplar okumasını isteyin. verimlilik sahte bir argüman. Bir sistem çağrısı veya bağlam anahtarı kolayca on binlerce saat kenesine mal olabilir, bir tampon üzerinden bir geçiş veri yolu hızında çalışır. Ağ programlarını optimize etmek istiyorsanız: sistem çağrılarının sayısını en aza indirin (daha büyük parçaları okuyarak / yazarak)
wildplasser

7
memset"Biraz daha fazla kontrol etme" nedeniyle biraz daha az verimli olabilecek fikir kesinlikle erken bir optimizasyon örneğidir: Bir CPU talimatını atlamaktan ne kadar kazanç elde ederseniz edin, taşınabilirliğinizi tehlikeye atabildiğinizde buna değmez. kodu. bzerokullanılmıyor ve onu kullanmamak için yeterli sebep bu.
dasblinkenlight

4
Genellikle bunun yerine bir başlatıcı `= {0}` ekleyebilir ve hiç bir işlevi çağıramazsınız. C yüzyılın başlarında yerel değişkenlerin ön bildirimini gerektirdiğinde bu daha kolay hale geldi. Yine de, gerçekten eski bazı kağıt eşyalar bir önceki yüzyılda hala sıkışmış durumda.
MSalters

1
@SSAnne hayır, ancak büyük olasılıkla, aşağıdaki yanıtlardan birinde belirtildiği gibi, etkilendiği kurs için önerilen bir kitaptan kaynaklanmıştır: stackoverflow.com/a/17097072/1428743
PseudoPsyche

Yanıtlar:


152

Ben tercih için herhangi bir neden göremiyorum bzeroüzerinde memset.

memsetise standart bir C fonksiyonudur bzerobir C standart fonksiyon olmamıştı. Bunun mantığı muhtemelen fonksiyonu kullanarak tam olarak aynı işlevselliği elde edebilmenizdir memset.

Şimdi verimlilikle ilgili olarak, derleyiciler , bir sabit tespit edildiğinde belirli bir uygulamaya geçiş yapan gccyerleşik uygulamaları kullanmak gibi . Yerleşik yapıları devre dışı bırakıldığında aynıdır .memset0glibc


Teşekkürler. Bu mantıklı. Bunun memsether zaman bu durumda kullanılması gerektiğinden emindim , ama neden kullanmadığımız konusunda kafası karışmıştı. Düşüncelerimi açıkladığın ve tekrar teyit ettiğin için teşekkürler.
PseudoPsyche

1
Bozuk bzerouygulamalarda birçok sorun yaşadım . Hizalanmamış dizilerde, sağlanan uzunluğu aşmak ve biraz daha fazla bayt sıfırlamak için kullanılır. Geçişten sonra böyle bir sorun yaşamadım memset.
rustyx

memset_sDerleyicinin hafızayı güvenlikle ilgili bir amaç için "ovma" çağrısını sessizce optimize etmediğinden emin olmak için hangisinin kullanılması gerektiğini unutmayın (örneğin, hassas bir bellek içeren bir hafıza alanını boşaltmak gibi) açık metin parolası gibi bir bilgi parçası).
Christopher Schultz

69

W. Richard Stevens'ın UNIX Ağ Programlamasını kullandığınızı (veya öğretmeninizden etkilendiğini) tahmin ediyorum . En güncel baskıda bile bzerosıkça kullanıyor memset. Kitap çok popüler, sanırım ağ programlamada bir deyim haline geldi, bu yüzden hala kullanıldığını görüyorsunuz.

Ben sopa ile olur memsetçünkü bzerokaldırılmış ve taşınabilirlik azaltır. Birini diğerinin üzerinde kullanmaktan gerçek kazançlar göreceğinizden şüpheliyim.


4
Doğru olurdun. Bu ders için ders kitaplarına gerek duymadık, ancak ders programını tekrar kontrol ettim ve UNIX Ağ Programlama gerçekten isteğe bağlı bir kaynak olarak listeleniyor. Teşekkürler.
PseudoPsyche

9
Aslında bundan daha kötü. POSIX.1-2001'de kullanımdan kaldırıldı ve POSIX.1-2008'de kaldırıldı .
paxdiablo

9
W. Richard Stevens tarafından UNIX Ağ Programcılığının üçüncü baskısının 8. sayfasından alıntı - Gerçekten de, TCPv3'ün yazarı, ilk baskının 10 tekrarında ikinci ve üçüncü argümanları taklit etmekle hata yaptı. AC derleyicisi bu hatayı yakalayamaz çünkü her iki oluşum aynıdır ... bir hataydı ve bzero kullanarak önlenebilir, çünkü işlev argümanları kullanılırsa iki argümanı bzero'ya değiştirmek her zaman C derleyicisi tarafından yakalanır. Ancak paxdiablo'nun işaret ettiği gibi, bzero kullanımdan kaldırılmıştır.
Aaron Newton

@AaronNewton, söylediklerini doğruladığı için bunu Michael'ın cevabına eklemelisin .
Synetech

52

Bence bir avantaj bzero()edemediği memset()sıfıra bellek ayarlama yapılıyor bir hata azaltılmış şans var olmasıdır.

Bir kereden fazla benzeyen bir hatayla karşılaştım:

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

Derleyici şikayet etmeyecektir (ancak bazı derleyicilerde bazı uyarı düzeylerinin yükseltilmesi olabilir) ve bunun etkisi belleğin temizlenmemesidir. Bu, nesneyi çöpe atmadığı için - sadece yalnız bırakıyor - hatanın bariz bir şeye tezahür etmemesi için iyi bir şans var.

bzero()Standart olmadığı gerçeği küçük bir tahriş edicidir. (FWIW, programlarımdaki çoğu işlev çağrısı standart değilse sürpriz olmazdım; aslında bu tür işlevleri yazmak benim işimdir).

Buradaki başka bir cevaba yapılan bir yorumda Aaron Newton, Unix Network Programming, Cilt 1, 3. Baskı, Stevens ve diğerleri, Bölüm 1.2'den (vurgu eklenmiştir) alıntı yaptı:

bzerobir ANSI C işlevi değildir. Erken Berkely ağ kodundan türetilmiştir. Bununla birlikte, ANSI C memsetişlevi yerine metin boyunca kullanıyoruz , çünkü bzerohatırlanması (sadece iki argümanla) memset(üç argümanla) daha kolaydır . Sockets API'sını destekleyen hemen hemen her satıcı aynı zamanda bzerobaşlığımızda bir makro tanımı sağlar unp.h.

Gerçekten de, TCPv3'ün yazarı [TCP / IP Illustrated, Cilt 3 - Stevens 1996] memsetilk baskıda 10 kez ikinci ve üçüncü argümanları değiştirme hatası yaptı . Her iki argüman da aynı tür olduğundan AC derleyicisi bu hatayı yakalayamıyor. (Aslında, ikinci bağımsız bir olduğu intve üçüncü bir argümandır size_ttipik olarak bir olan unsigned int, ancak değerleri belirtilen 0 ve 16, sırası ile, yine Tartışmanın diğer tip için kabul edilebilir değildir.) İçin ara memsetçalışmaya, sadece a nedeniyle soket işlevlerinin birkaçı aslında bir İnternet soketi adres yapısının son 8 baytının 0 olarak ayarlanmasını gerektirir. Bununla birlikte, bu bir hataydı ve bir tanesi kullanılarak önlenebilir bzero, çünkü bzeroişlev argümanları kullanılırsa iki argümanın değiştirilmesi her zaman C derleyicisi tarafından yakalanır.

Ayrıca, çağrıların büyük çoğunluğunun memset()belleğe sıfır olduğuna inanıyorum, neden bu kullanım durumuna göre uyarlanmış bir API kullanmıyorsunuz?

Bunun olası bir dezavantajı bzero(), derleyicilerin optimizasyon olasılığının daha yüksek olabileceğidir, memcpy()çünkü standarttır ve bu nedenle onu tanımak için yazılabilirler. Ancak, doğru kodun optimize edilmiş yanlış koddan daha iyi olduğunu unutmayın. Çoğu durumda, kullanmak bzero(), programınızın performansı üzerinde belirgin bir etkiye neden olmaz ve bu bzero(), genişleyen bir makro veya satır içi işlev olabilir memcpy().


Evet, sanırım böyle bir sınıf ortamında çalışırken öğrenciler için daha az kafa karıştırıcı hale getirmek için bir sebep olabilir. Ancak profesörümde böyle olduğunu düşünmüyorum. Çok büyük bir RTFM öğretmeniydi. Kılavuz tarafından cevaplanabilecek bir sorunuz varsa, projektördeki man sayfalarını sınıfta çeker ve size gösterir. Herkesin aklına, kılavuzun okunmak için orada olduğu ve sorularınızın çoğunu cevapladığı hakkında çok şey katmakla ilgiliydi. Diğer profesörlerin aksine bunun için minnettarım.
PseudoPsyche

5
Bunun sınıf dışında bile yapılabilecek bir argüman olduğunu düşünüyorum - bu hatayı üretim kodunda gördüm. Yapmam kolay bir hata olarak bana vurdu. Ayrıca, memset()çağrıların büyük çoğunluğunun başka bir argüman olduğunu düşündüğüm bir bellek bloğunu sıfırlamak olduğunu tahmin ediyorum bzero(). bzero()Zaten 'b' ne anlama geliyor ?
Michael Burr

7
+1. Bu memset, "tampon, buffer_size" gibi ortak bir parametre sırasını ihlal ederek özellikle hataya açık IMO yapar.
jamesdlin

Pascal'da buna "fillchar" diyerek bundan kaçınırlar ve bir karakter alır. Çoğu C / C ++ derleyicisi bunu alır. Bu da derleyicilerin neden "bir baytın beklendiği yerde 32/64 bitlik bir işaretçiyi geçtiğinizi" söylemediğini ve derleyici hatalarında sizi sıkıca tekmelediğini merak ediyor.
Mó13

1
@ İkinci ve üçüncü argüman yanlış sırada; alıntılanan işlev çağrısı tam olarak hiçbir şey yapmaz
Ichthyo

4

Bzero vs. memset argümanından bahsetmek istedim. Ltrace'i takın ve kaputun altında ne yaptığını karşılaştırın. Libc6 (2.19-0ubuntu6.6) içeren Linux'ta yapılan çağrılar tamamen aynıdır (üzerinden 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)

Bana libc'nin derin bağırsaklarında veya herhangi bir sayıda çekirdek / sistem çağrısı arayüzünde çalışmadığım sürece , onlar hakkında endişelenmem gerekmediği söylendi . Endişelenmem gereken tek şey, çağrının arabellek sıfırlama gereksinimini karşılamasıdır. Diğerleri hangisinin diğerine tercih edildiğinden bahsetti, bu yüzden burada duracağım.


Bunun nedeni, GCC'nin bazı sürümlerinin memset(ptr, 0, n)gördükleri zaman için kod yayınlayabilmeleri bzero(ptr, n)ve bunları satır içi koda dönüştürememeleri nedeniyle olur.
zwol

@zwol Aslında bir makro.
SS Anne

1
@SSAnne gcc 9.3 bilgisayarımdaki bu dönüşümü, sistem başlıklarındaki makroların yardımı olmadan yapar. extern void bzero(void *, size_t); void clear(void *p, size_t n) { bzero(p, n); }çağrı yapar memset. (Dahil stddef.hiçin size_tmüdahale edebilecek başka bir şey olmadan.)
Zwol

4

Muhtemelen olmamalı kullanmak bzeroaslında standart C değil, bu bir POSIX şeydi.

Ve kelime "oldu" notu o - bu oldu kaldırılmış POSIX.1-2001 ve kaldırılır daha iyi standart C fonksiyonu kullanılarak kapalı olmana çok memset'in gözönünde bulundurularak POSIX.1-2008 içinde.


Standart C ile ne demek istiyorsun? Standart C kütüphanesinde bulunmuyor mu?
Koray Tugay

@Koray, standart C, ISO standardı anlamına gelir ve evet, bzerobunun bir parçası değildir.
paxdiablo

Hayır, demek istediğim, herhangi bir standartla ne demek istediğini bilmiyorum. ISO standardı standart C kütüphanesi anlamına mı geliyor? Bu dil ile mi geliyor? Orada olacağını bildiğimiz minimal kütüphane mi?
Koray Tugay

2
@Koray, ISO, C standardından sorumlu, şu anki C11 ve daha önceleri C99 ve C89'dan sorumlu standartlar kuruluşudur. C olarak değerlendirilmek için bir uygulamanın izlemesi gereken kuralları ortaya koyarlar. Evet, eğer standart, bir uygulamanın memset sağlaması gerektiğini söylüyorsa, sizin için orada olacaktır. Aksi takdirde, C değil.
paxdiablo

2

Memset işlevi için ikinci argüman bir int, üçüncü argüman ise size_t,

void *memset(void *s, int c, size_t n);

Bu tipik olarak bir olur unsigned int, ancak 0 and 16sırasıyla ikinci ve üçüncü argüman gibi değerler 16 ve 0 olarak yanlış sırada girilirse, böyle bir memset çağrısı hala işe yarayabilir, ancak hiçbir şey yapmaz. Çünkü başlatılacak bayt sayısı olarak belirtilir 0.

void bzero(void *s, size_t n)

Bzero kullanılarak böyle bir hata önlenebilir, çünkü fonksiyon argümanları kullanılırsa iki argümanın bzero'ya değiştirilmesi her zaman C derleyicisi tarafından yakalanır.


1
Çağrıyı "bu belleği bu boyut için bu değere ayarla" olarak düşünürseniz veya size prototipi veren bir IDE'niz varsa veya ne olduğunuzu bilseniz bile, böyle bir hata memset ile önlenebilir. yapıyor :-)
paxdiablo

Kabul et, ancak bu işlev bu tür akıllı IDE'lerin destek için mevcut olmadığı zamanda oluşturuldu.
havish

2

Kısacası: memset daha fazla montaj işlemi gerektirir bzero.

Bu kaynak: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown


Evet, bu OP'de bahsettiğim bir şey. Aslında o sayfaya da bağlandım. Bazı derleyici optimizasyonları nedeniyle çok fazla fark yaratmadığı anlaşılıyor. Daha fazla bilgi için ouah tarafından kabul edilen cevaba bakınız.
PseudoPsyche

6
Bu sadece memset'in bir çöp uygulamasının yavaş olduğunu gösterir. MacOS X ve diğer bazı sistemlerde memset, kullandığınız işlemciye bağlı olarak önyükleme sırasında ayarlanan kodu kullanır, vektör kayıtlarını tam olarak kullanır ve büyük boyutlar için son biti almak için akıllıca alma talimatlarını kullanır hız.
gnasher729

daha az talimat daha hızlı yürütme anlamına gelmez. Aslında optimizasyonlar genellikle döngü açma, işlev satır içi, döngü hizalama nedeniyle ikili boyutu ve talimat sayısını artırır ... İyi optimize edilmiş kodlara bakın ve boktan uygulamalardan çok daha fazla talimatı olduğunu
göreceksiniz

2

İstediğin gibi olsun. :-)

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

Bunu not et:

  1. Orijinal bzerohiçbir şey memsetdöndürmez, geçersiz işaretçi ( d) döndürür . Bu, tanımda geçersiz kılınan typecast eklenerek giderilebilir.
  2. #ifndef bzeromevcut olsa bile orijinal işlevi gizlemenizi engellemez. Bir makronun varlığını test eder. Bu çok fazla karışıklığa neden olabilir.
  3. Bir makroya bir işlev işaretçisi oluşturmak imkansızdır. Kullanırken bzerofonksiyon göstergeler vasıtasıyla bu işe yaramaz.

1
Bununla ilgili sorun nedir, @ Leeor? Makrolar için genel antipati? Yoksa bu makronun işlevle karıştırılabileceği (ve muhtemelen gizleyebileceği) gerçeğini beğenmiyor musunuz?
Palec

1
@Palec, ikincisi. Makro olarak yeniden tanımlamayı gizlemek çok karışıklığa neden olabilir. Bu kodu kullanan başka bir programcı bir şey kullandığını düşünüyor ve bilmeden diğerini kullanmaya zorlanıyor. Bu bir saatli bomba.
Leeor

1
Başka bir düşünce verdikten sonra, bunun gerçekten kötü bir çözüm olduğunu kabul ediyorum. Diğer şeylerin yanı sıra teknik bir neden buldum: bzeroİşlev işaretçileriyle kullanırken, bu işe yaramaz.
Palec

Gerçekten makronuza başka bir şey demeliydiniz bzero. Bu bir vahşet.
Dan Bechard

-2

memset 3 parametre alır, bzero bellekte 2 ek parametre 4 bayt alır ve çoğu zaman her şeyi 0 olarak ayarlamak için kullanılır

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.