memset () o inicialización de valor para poner a cero una estructura?

En la progtwigción de API de Win32, es típico usar C struct con múltiples campos. Por lo general, solo un par de ellos tienen valores significativos y todos los demás deben ser eliminados. Esto se puede lograr de cualquiera de las dos maneras:

 STRUCT theStruct; memset( &theStruct, 0, sizeof( STRUCT ) ); 

o

 STRUCT theStruct = {}; 

La segunda variante se ve más limpia, es un trazador de líneas único, no tiene ningún parámetro que pueda ser mal escrito y puede generar un error.

¿Tiene algún inconveniente en comparación con la primera variante? ¿Qué variante usar y por qué?

Esos dos constructos son muy diferentes en su significado. El primero usa una función memset , que está destinada a establecer un buffer de memoria a cierto valor . El segundo para inicializar un objeto . Déjame explicarlo con un poco de código:

Supongamos que tiene una estructura que tiene miembros solo de tipos de POD

 struct POD_OnlyStruct { int a; char b; }; POD_OnlyStruct t = {}; // OK POD_OnlyStruct t; memset(&t, 0, sizeof t); // OK as well 

En este caso, escribir un POD_OnlyStruct t = {} o POD_OnlyStruct t; memset(&t, 0, sizeof t) POD_OnlyStruct t; memset(&t, 0, sizeof t) no hace mucha diferencia, ya que la única diferencia que tenemos aquí es que los bytes de alineación se establecen en valor cero en el caso de memset utilizado. Como no tiene acceso a esos bytes normalmente, no hay diferencia para usted.

Por otro lado, ya que ha etiquetado su pregunta como C ++, intentemos con otro ejemplo, con tipos de miembros diferentes de POD :

 struct TestStruct { int a; std::string b; }; TestStruct t = {}; // OK { TestStruct t1; memset(&t1, 0, sizeof t1); // ruins member 'b' of our struct } // Application crashes here 

En este caso, usar una expresión como TestStruct t = {} es buena, y usar un memset en ella dará lugar a la falla. TestStruct es lo que sucede si usa memset : se memset un objeto de tipo TestStruct , creando así un objeto de tipo std::string , ya que es un miembro de nuestra estructura. A continuación, memset establece la memoria donde el objeto b se encuentra a cierto valor, digamos cero. Ahora, una vez que nuestro objeto TestStruct quede fuera del scope, se destruirá y cuando llegue el momento en que el miembro es std::string b verá un locking, ya que todas las estructuras internas de ese objeto fueron arruinadas por el memset .

Entonces, la realidad es que esas cosas son muy diferentes , y aunque a veces necesitas memset una estructura completa para ceros en ciertos casos, siempre es importante asegurarte de que entiendes lo que estás haciendo, y no cometer un error como en nuestra segundo ejemplo.

Mi voto: use memset en objetos solo si es necesario y use la inicialización predeterminada x = {} en todos los demás casos.

Dependiendo de los miembros de la estructura, las dos variantes no son necesariamente equivalentes. memset establecerá la estructura a todos los bits cero mientras que la inicialización de valores inicializará todos los miembros al valor cero. El estándar C garantiza que sean los mismos solo para los tipos integrales, no para los valores de punto flotante o los punteros.

Además, algunas API requieren que la estructura realmente se configure en todos los bits cero. Por ejemplo, la API de socket de Berkeley usa estructuras de forma polimórfica, y allí es importante realmente establecer toda la estructura a cero, no solo los valores que son evidentes. La documentación API debe decir si la estructura realmente necesita ser todo-bits-cero, pero podría ser deficiente.

Pero si ninguno de estos, o un caso similar, se aplica, entonces depende de usted. Al definir la estructura, preferiría la inicialización del valor, ya que eso comunica la intención más claramente. Por supuesto, si necesita poner a cero una estructura existente, memset es la única opción (bueno, aparte de inicializar cada miembro a cero manualmente, pero eso normalmente no se haría, especialmente para estructuras grandes).

Si su estructura contiene cosas como:

 int a; char b; int c; 

Luego se insertarán bytes de relleno entre “b” y “c”. memset () los pondrá a cero, a la inversa no, por lo que habrá 3 bytes de basura (si sus datos son 32 bits). Si tiene la intención de usar su estructura para leer / escribir desde un archivo, esto podría ser importante.

Usaría la inicialización de valores porque parece limpia y menos propensa a errores como mencionaste. No veo ningún inconveniente en hacerlo.

Puede confiar en memset para memset a cero la estructura después de que se haya utilizado.

no es que sea común, pero supongo que la segunda forma también tiene el beneficio de inicializar flotadores a cero. Mientras que hacer un memset ciertamente no

En algunos comstackdores STRUCT theStruct = {}; se traduciría a memset( &theStruct, 0, sizeof( STRUCT ) ); en el ejecutable Algunas funciones de C ya están vinculadas para configurar el tiempo de ejecución, por lo que el comstackdor tiene disponibles estas funciones de biblioteca, como memset / memcpy.

La inicialización del valor porque se puede hacer en tiempo de comstackción.
También correctamente 0 inicializa todos los tipos de POD.

El memset () se hace en tiempo de ejecución.
También usar memset () es sospechoso si la estructura no es POD.
No inicializa correctamente (a cero) tipos no int.

Si hay muchos miembros de puntero y es probable que agregue más en el futuro, puede ser útil usar memset. Combinado con las assert(struct->member) apropiadas, puede evitar lockings aleatorios al intentar deferenciar un puntero malo que olvidó inicializar. Pero si no eres tan olvidadizo como yo, entonces la inicialización de miembros es probablemente la mejor.

Sin embargo , si su estructura se usa como parte de una API pública, debe obtener el código del cliente para usar memset como requisito. Esto ayuda con futuras pruebas, ya que puede agregar nuevos miembros y el código del cliente NULL automáticamente en la llamada memset, en lugar de dejarlos en un estado no inicializado (posiblemente peligroso). Esto es lo que haces cuando trabajas con estructuras de socket, por ejemplo.