Por que usar bzero sobre memset?
Em uma Programação de Sistemas, classe I tomou este semestre anterior, tivemos que implementar uma base de cliente/servidor em C. Quando inicializar as estruturas, como sock_addr_in
, ou char buffers (que usamos para enviar dados e para trás entre o cliente e o servidor), o professor ensinou-nos a usar apenas bzero
e não memset
para inicializá-los. Ele nunca explicou porquê, e estou curioso se há uma razão válida para isto?
bzero
é mais eficiente devido ao facto de que só vai estar a zerar a memória, por isso não tem de fazer nenhuma verificação adicional que memset
possa fazer. Isso ainda não parece necessariamente uma razão para absolutamente não usar memset
para zerar a memória.
bzero
é considerada obsoleta e, além disso, não é uma função C padrão. De acordo com o manual, memset
é preferível a bzero
para isto. razao. Então porque é que ainda queres usar bzero
em memset
? Só pelos ganhos de eficiência, ou é algo mais? Da mesma forma, Quais são os benefícios de memset
sobre bzero
que o tornam a opção de facto preferida para programas mais recentes?
8 answers
Não vejo razão para preferir {[[0]} mais memset
.
memset
é uma função C padrão enquanto bzero
nunca foi uma função C padrão. A lógica é provavelmente porque você pode alcançar exatamente a mesma funcionalidade usando a função memset
.
Agora em relação à eficiência, compiladores como gcc
usam implementações de builtin para {[1] } que mudam para uma implementação particular quando uma constante 0
é detectada. O mesmo para glibc
quando as compilações estão desactivadas.
memset
, mesmo na edição mais atualizada. O livro é tão popular, que eu acho que se tornou um idioma na programação de rede e é por isso que você ainda o vê usado.
Eu ficaria com memset
simplesmente porque {[[0]} está desactualizado e reduz a portabilidade. Duvido que vejas algum ganho real em usar um em vez do outro.
bzero()
tem sobre memset()
para ajustar a memória a zero é que há uma possibilidade reduzida de um erro ser cometido.
Mais de uma vez deparei-me com um insecto que parecia ...
memset(someobject, size_of_object, 0); // clear object
O compilador não vai reclamar (embora talvez aumentando alguns níveis de aviso pode em alguns compiladores) e o efeito será que a memória não é limpa. Porque isto não destrói o objecto-deixa-o em paz-há uma boa hipótese de que o insecto pode não se manifestar em nada óbvio.
O facto de bzero()
não ser o padrão é um pouco irritante. (FWIW, eu não ficaria surpreso se a maioria das chamadas de função em meus programas não são padrão; na verdade, escrever tais funções é tipo o meu trabalho).
bzero
não é uma função ANSI C. É derivado do Berkely precoce código de rede. No entanto, utilizamo-lo em todo o texto, em vez disso da função ANSI Cmemset
, porquebzero
é mais fácil de lembrar (com apenas dois argumentos) do quememset
(com três argumentos). Quase cada fornecedor que suporta a API sockets também fornecebzero
, e se não, nós fornecemos uma macro definição no nosso cabeçalhounp.h
.De facto, o autor do TCPv3 [TCP / IP Illustrated, Volume 3-Stevens 1996] cometeu o erro de trocar o segundo e terceiro argumento para
memset
em 10 ocorrências na primeira impressão . Um compilador C não consegue apanhar este erro porque ambos os argumentos são do mesmo tipo. (Na verdade, o segundo argumento é umint
e o terceiro argumento ésize_t
, que é tipicamente umunsigned int
, mas os valores especificados, 0 e 16, respectivamente, ainda são aceitáveis para o outro tipo de argumento.) A chamada para {[5] } ainda funcionou, porque apenas algumas das funções do 'socket' requerem que o os últimos 8 bytes de uma estrutura de endereços de socket de Internet são definidos como 0. No entanto, foi um erro, que poderia ser evitado usandobzero
, porque trocar os dois argumentos parabzero
será sempre capturada pelo compilador C se forem utilizados protótipos de função.
Eu também acredito que a grande maioria das chamadas para {[[2]} são para memória zero, então porque não usar uma API que é adaptada para esse caso de uso?
Uma possível desvantagem para bzero()
é que os compiladores podem ser mais provavelmente para otimizar memcpy()
porque é padrão e então eles podem ser escritos para reconhecê-lo. No entanto, tenha em mente que o código correto ainda é melhor do que o código incorreto que foi otimizado. Na maioria dos casos, o uso de bzero()
não irá causar um impacto notável no desempenho do seu programa, e que bzero()
pode ser uma função macro ou inline que se expande para memcpy()
.
Em resumo: memset
necessita de mais operações de montagem do que bzero
.
Esta é a fonte: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown
E note que a palavra " foi " - foi depreciado em POSIX.1-2001 e removido em POSIX.1-2008 em deferência ao memset para que esteja melhor usando a função C padrão.
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)
Disseram-me que a menos que esteja a trabalhar nas profundezas da libc ou em qualquer número de interface kernel/syscall, Não tenho de me preocupar com eles. A única coisa com que me devo preocupar é que a chamada satisfaça a exigência de zero no buffer. Outros têm mencionado sobre qual deles é melhor do que o outro, então vou parar aqui.
Para a função memset, o segundo argumento é um int
e o terceiro argumento é size_t
,
void *memset(void *s, int c, size_t n);
Que é tipicamente um unsigned int
, mas se os valores como, 0 and 16
para o segundo e terceiro argumentos respectivamente são inseridos na ordem errada como 16 e 0 então,
tal chamada para memset ainda pode funcionar, mas não fará nada. Porque o número de bytes a inicializar são especificados como 0
.
void bzero(void *s, size_t n)
Tal erro pode ser evitado usando bzero, porque trocar os dois argumentos para bzero será sempre apanhado pelo compilador C se forem utilizados protótipos de função.
#ifndef bzero
#define bzero(d,n) memset((d),0,(n))
#endif
Note que:
- o
bzero
original não devolve nada,memset
devolve o cursor vazio (d
). Isto pode ser corrigido adicionando o typecast ao vazio na definição. -
#ifndef bzero
não o impede de esconder a função original mesmo que ela exista. Testa a existência de uma macro. Isto pode causar muita confusão.
É impossível criar um ponteiro de função para uma macro. Ao utilizar
bzero
através da função apontadores, isto não vai funcionar.