array c++ c struct

c++ - array - typedef struct c



Inicializando un objeto a todos los ceros (12)

A menudo, la inicialización válida de las estructuras de datos es establecer todos los miembros a cero. Incluso cuando se programa en C ++, es posible que se necesite una interfaz con una API externa para este caso.

¿Hay alguna diferencia práctica entre:

some_struct s; memset(&s, 0, sizeof(s));

y simplemente

some_struct s = { 0 };

¿Las personas se encuentran utilizando ambos, con un método para elegir cuál es más apropiado para una aplicación determinada? (Afortunadamente, se entiende que esto solo es aplicable actualmente a las estructuras POD; se produciría todo tipo de estragos si hubiera una cadena C ++ std :: en esa estructura).

Para mí, como un programador de C ++ que no usa mucho memset, nunca estoy seguro de la firma de la función, así que el segundo ejemplo es simplemente más fácil de usar, además de ser menos tipeo, más compacto y tal vez incluso más es obvio ya que dice "este objeto se inicializa a cero" en la declaración en lugar de esperar la siguiente línea de código y ver "oh, este objeto se inicializa en cero".

Al crear clases y estructuras en C ++, tiendo a usar listas de inicialización; Tengo curiosidad sobre los pensamientos de la gente sobre las dos inicializaciones de "estilo C" anteriores en lugar de una comparación con lo que está disponible en C ++ ya que sospecho que muchos de nosotros interactuamos con bibliotecas C incluso si codificamos principalmente en C ++.

Editar: Neil Butterworth planteó esta pregunta , en seguimiento, que creo que es un corolario interesante de esta pregunta.


Esperemos que se entienda que esto solo está disponible actualmente para las estructuras POD; obtendría un error de compilación si hubiera una cadena C ++ std :: en esa estructura.

No, no lo harás Si usas memset para memset , en el mejor de los casos solo memset y, en el peor de los casos, obtendrás un galimatías. La forma = { } se puede usar perfectamente bien en estructuras que no sean POD, siempre que sean agregados. La = { } forma es la mejor manera de tomar en C ++. Tenga en cuenta que no hay ninguna razón en C ++ para poner ese 0 en él, ni se recomienda, ya que reduce drásticamente los casos en los que se puede utilizar

struct A { std::string a; int b; }; int main() { A a = { 0 }; A a = { }; }

El primero no hará lo que quieras: intentará crear una std::string partir de una cadena C dado un puntero nulo a su constructor. El segundo, sin embargo, hace lo que quiere: crea una cadena vacía.


Creo que la inicialización habla mucho más claro de lo que realmente estás haciendo. Estás inicializando la estructura. Cuando el nuevo estándar esté fuera, esa forma de inicialización se utilizará aún más (la inicialización de contenedores con {} es algo que esperamos con impaciencia). La manera memset es ligeramente más propensa a errores, y no comunica claramente lo que estás haciendo. Eso podría no representar mucho mientras se programa solo, pero significa mucho cuando se trabaja en equipo.

Para algunas personas que trabajan con c ++, memset, malloc & co. son criaturas bastante esotéricas. Me he encontrado con algunos yo mismo.


Dependiendo de la optimización del compilador, puede haber algún umbral por encima del cual memset sea más rápido, pero que normalmente estaría muy por encima del tamaño normal de las variables basadas en la pila. Usar memset en un objeto C ++ con una tabla virtual es, por supuesto, malo.


El mejor método para borrar estructuras es establecer cada campo individualmente:

struct MyStruct { std::string name; int age; double checking_account_balance; void clear(void) { name.erase(); age = 0; checking_account_balance = 0.0; } };

En el ejemplo anterior, se define un método clear para establecer todos los miembros a un estado o valor conocido. Los memset y std::fill pueden no funcionar debido a std::string y tipos double . Un programa más robusto borra cada campo individualmente.

Prefiero tener un programa más sólido que pasar menos tiempo escribiendo.


La única diferencia práctica es que the ={0}; la sintaxis es un poco más clara al decir "inicializar esto para que esté vacío" (al menos me parece más claro).

Puramente teórico, hay algunas situaciones en las que memset podría fallar, pero hasta donde yo sé, realmente son solo eso: teóricas. OTOH, dado que es inferior tanto desde un punto de vista teórico como práctico, me es difícil descifrar por qué alguien querría usar memset para esta tarea.


La función bzero es otra opción.

#include <strings.h> void bzero(void *s, size_t n);


Nunca he entendido la misteriosa bondad de poner todo a cero, lo que incluso si se define, parece poco probable que sea deseable. Como esto se etiqueta como C ++, la solución correcta para la inicialización es dar a la estructura o clase un construtor.


Si la estructura contiene punteros, el valor de todos los bits cero producidos por memset puede no significar lo mismo que asignarle un 0 en el código C (o C ++), es decir, un puntero NULL .

(También podría ser el caso con floats y doubles , pero nunca me he encontrado. Sin embargo, no creo que los estándares les garanticen que se vuelvan cero con memset tampoco).

Editar: desde una perspectiva más pragmática, aún diría que no use memset cuando sea posible evitarlo, ya que es una llamada a función adicional, más larga de escribir y (en mi opinión) menos clara en intención que = { 0 } .


some_struct s = { 0 }; se garantiza que funciona; memset basa en los detalles de implementación y es mejor evitarlo.


En CI, prefiero usar {0,} al memset equivalente (). Sin embargo, gcc advierte sobre este uso :( Detalles aquí: http://www.pixelbeat.org/programming/gcc/auto_init.html

En C ++ generalmente son equivalentes, pero como siempre con C ++, hay casos de esquina que se deben considerar (señalados en otras respuestas).


memset es prácticamente nunca la manera correcta de hacerlo. Y sí, hay una diferencia práctica (ver abajo).

En C ++ no todo se puede inicializar con el literal 0 (los objetos de los tipos de enumeración no pueden ser), razón por la cual en C ++ el lenguaje común es

some_struct s = {};

mientras que en C el modismo es

some_struct s = { 0 };

Tenga en cuenta que en C, el = { 0 } es lo que se puede llamar el inicializador de cero universal . Se puede usar con objetos de prácticamente cualquier tipo, ya que los inicializadores {} -encerrados también se permiten con objetos escalares

int x = { 0 }; /* legal in C (and in C++) */

lo que hace que = { 0 } útil en el código C genérico independiente del tipo (por ejemplo, macros independientes del tipo).

El inconveniente del inicializador = { 0 } en C89 / 90 y C ++ es que solo se puede usar como parte de la declaración. (C99 corrigió este problema introduciendo literales compuestos . También se está llegando a C ++ una funcionalidad similar). Por esta razón, puede ver que muchos programadores usan memset para poner a cero algo en el medio de C89 / 90 o C ++ el código. Sin embargo, diría que la forma correcta de hacerlo es aún sin memset pero más bien con algo así como

some_struct s; ... { const some_struct ZERO = { 0 }; s = ZERO; } ...

es decir, al introducir un bloque "ficticio" en el medio del código, aunque a primera vista no parezca demasiado bonito. Por supuesto, en C ++ no hay necesidad de introducir un bloque.

En cuanto a la diferencia práctica ... Es posible que algunas personas digan que memset producirá los mismos resultados en la práctica, ya que en la práctica el patrón de bits físicos a cero es lo que se usa para representar valores cero para todos los tipos. Sin embargo, esto generalmente no es cierto. Un ejemplo inmediato que demostraría la diferencia en una implementación típica de C ++ es un tipo de puntero a miembro de datos

struct S; ... int S::*p = { 0 }; assert(p == NULL); // this assertion is guaranteed to hold memset(&p, 0, sizeof p); assert(p == NULL); // this assertion will normally fail

Esto sucede porque una implementación típica generalmente usa el patrón de bits de uno a uno ( 0xFFFF... ) para representar el puntero nulo de este tipo. El ejemplo anterior muestra una diferencia práctica de la vida real entre un memset cero y un inicializador normal = { 0 } .


Encontré una buena solución para ser:

template<typename T> void my_zero(T& e) { static T dummy_zero_object; e = dummy_zero_object; } my_zero(s);

Esto hace lo correcto no solo para tipos fundamentales y tipos definidos por el usuario, sino que también inicializa a cero los tipos para los que se define el constructor predeterminado, pero no inicializa todas las variables miembro, especialmente las clases que contienen miembros de union no triviales.