que - strcat
¿Por qué usar bzero sobre memset? (8)
En una clase de programación de sistemas que tomé este semestre anterior, tuvimos que implementar un cliente / servidor básico en C. Al inicializar las estructuras, como sock_addr_in
, o los búferes de char (que solíamos enviar y sock_addr_in
datos entre el cliente y el servidor) el El profesor nos indicó que solo bzero
y no memset
para inicializarlos. Él nunca explicó por qué, y tengo curiosidad de saber si hay una razón válida para esto.
Veo aquí: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown que bzero
es más eficiente debido al hecho de que solo va a poner a cero la memoria, por lo que no funciona Tengo que hacer cualquier verificación adicional que memset
pueda hacer. Sin embargo, eso no necesariamente parece una razón para no usar memset
para poner a cero la memoria.
bzero
se considera obsoleto y, además, no es una función C estándar. De acuerdo con el manual, memset
es preferido sobre bzero
por este motivo. Entonces, ¿por qué querrías seguir usando bzero
sobre memset
? Solo por las ganancias de eficiencia, ¿o es algo más? Del mismo modo, ¿cuáles son los beneficios de memset
over bzero
que lo convierten en la opción preferida de facto para los programas más nuevos?
Hazlo como quieras. :-)
#ifndef bzero
#define bzero(d,n) memset((d),0,(n))
#endif
Tenga en cuenta que:
- El
bzero
originalbzero
devuelve nada,memset
devuelve el puntero void (d
). Esto puede solucionarse agregando el tipocast a nulo en la definición. -
#ifndef bzero
no le impide ocultar la función original, incluso si existe. Prueba la existencia de una macro. Esto puede causar mucha confusión. - Es imposible crear un puntero a una macro. Al usar
bzero
través de punteros de función, esto no funcionará.
La única ventaja que creo que bzero()
tiene sobre memset()
para establecer la memoria en cero es que hay una posibilidad reducida de cometer un error.
Más de una vez me encontré con un error que parecía:
memset(someobject, size_of_object, 0); // clear object
El compilador no se quejará (aunque tal vez genere algunos niveles de advertencia en algunos compiladores) y el efecto será que la memoria no se borrará. Como esto no destruye el objeto, simplemente lo deja en paz, existe la posibilidad de que el error no se manifieste en algo obvio.
El hecho de que bzero()
no sea estándar es un irritante menor. (FWIW, no me sorprendería si la mayoría de las llamadas de función en mis programas no son estándar, de hecho, la escritura de tales funciones es una especie de trabajo).
En un comentario a otra respuesta aquí, Aaron Newton citó lo siguiente de Unix Network Programming, Volumen 1, 3ra Edición por Stevens, et al., Sección 1.2 (énfasis agregado):
bzero
no es una función ANSI C. Se deriva del código de red de Berkely. Sin embargo, lo usamos en todo el texto, en lugar de la funciónmemset
ANSI C, porquebzero
es más fácil de recordar (con solo dos argumentos) quememset
(con tres argumentos). Casi todos los proveedores que admiten sockets API también proporcionanbzero
, y si no, proporcionamos una definición de macro en nuestro encabezadounp.h
De hecho, el autor de TCPv3 [TCP / IP Illustrated, Volumen 3 - Stevens 1996] cometió el error de intercambiar los argumentos segundo y tercero a
memset
en 10 ocurrencias en la primera impresión . El compilador de CA no puede detectar este error porque ambos argumentos son del mismo tipo. (En realidad, el segundo argumento es unint
y el tercer argumento essize_t
, que normalmente esunsigned int
, pero los valores especificados, 0 y 16, respectivamente, siguen siendo aceptables para el otro tipo de argumento.) La llamada amemset
todavía funcionó, porque solo algunas de las funciones de socket realmente requieren que los 8 bytes finales de una estructura de direcciones de socket de Internet se establezcan en 0. Sin embargo, era un error, y uno que se podía evitar usandobzero
, porque intercambiaba los dos argumentos tobzero
siempre será capturado por el compilador C si se usan prototipos de funciones.
También creo que la gran mayoría de las llamadas a memset()
son cero memoria, ¿por qué no utilizar una API que se adapta a ese caso de uso?
Un posible inconveniente de bzero()
es que es más probable que los compiladores optimicen memcpy()
porque es estándar y por lo tanto se pueden escribir para reconocerlo. Sin embargo, tenga en cuenta que el código correcto es aún mejor que el código incorrecto que se ha optimizado. En la mayoría de los casos, usar bzero()
no causará un impacto notable en el rendimiento de su programa, y ese bzero()
puede ser una función macro o en línea que se expande a memcpy()
.
No veo ninguna razón para preferir bzero
a memset
.
memset
es una función C estándar, mientras que bzero
nunca ha sido una función estándar C. El motivo es probablemente porque puede lograr exactamente la misma funcionalidad usando la función memset
.
Ahora, con respecto a la eficiencia, los compiladores como gcc
usan implementaciones memset
para memset
que cambian a una implementación particular cuando se detecta una constante 0
. Lo mismo para glibc
cuando los builtins están deshabilitados.
Para la función memset, el segundo argumento es un int
y el tercer argumento es size_t
,
void *memset(void *s, int c, size_t n);
que normalmente es un unsigned int
, pero si los valores como 0 and 16
para el segundo y tercer argumento, respectivamente, se ingresan en un orden incorrecto como 16 y 0, tal llamada a memset puede seguir funcionando, pero no hará nada. Porque el número de bytes para inicializar se especifica como 0
.
void bzero(void *s, size_t n)
Tal error puede evitarse usando bzero, ya que el compilador C siempre captará el intercambio de los dos argumentos a bzero si se usan prototipos de función.
Probablemente no deberías usar bzero
, en realidad no es C estándar, era algo POSIX.
Y tenga en cuenta que la palabra "era" - fue desaprobada en POSIX.1-2001 y eliminada en POSIX.1-2008 en deferencia a memset, por lo que es mejor que utilice la función C estándar.
Quería mencionar algo sobre el argumento bzero vs. memset. Instale ltrace y luego compare lo que hace debajo del capó. En Linux con libc6 (2.19-0ubuntu6.6), las llamadas realizadas son exactamente las mismas (a través de 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)
Me han dicho que a menos que esté trabajando en las entrañas profundas de libc o cualquier número de interfaz kernel / syscall, no tengo que preocuparme por ellos. Todo lo que debería preocuparme es que la llamada satisfaga el requisito de poner a cero el buffer. Otros han mencionado sobre cuál es preferible sobre el otro, así que me detendré aquí.
Supongo que usaste (o tu profesor fue influenciado por) Programación de red UNIX por W. Richard Stevens. Utiliza bzero
frecuencia en lugar de memset
, incluso en la edición más actualizada. El libro es tan popular, creo que se ha convertido en una expresión idiomática en la programación de redes, razón por la cual todavía se ve utilizado.
Me quedaría con memset
simplemente porque bzero
está en desuso y reduce la portabilidad. Dudo que veas algún beneficio real al usar uno sobre el otro.
En resumen: memset
requiere más operaciones de ensamblaje y luego bzero
.
Esta es la fuente: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown