sizeof style: sizeof (type) o sizeof variable?

22

He visto dos estilos de uso sizeofpara operaciones relacionadas con la memoria (como en memseto malloc):

  • sizeof(type)y
  • sizeof variable o sizeof(variable)

¿Cuál preferiría, o usaría una mezcla de los dos estilos, y cuándo usaría cada estilo? ¿Cuáles son los pros y los contras de cada estilo y cuándo los usa?

Como ejemplo, puedo ver el siguiente par de situaciones en las que un estilo ayuda y el otro no:

Cuando obtienes el puntero indirectamente incorrecto:

type *var;
...
memset(var, 0, sizeof var);    /* oops */

Cuando el tipo cambia:

new_type var;    /* changed from old_type to new_type */
...
memset(&var, 0, sizeof(old_type));    /* oops */
congusbongus
fuente

Respuestas:

30

Yo preferimos sizeof(variable)más sizeof(type). Considerar:

int a1;
float a2;
memset(&a1,0,sizeof(a1));
memset(&a2,0,sizeof(a2));

vs.

int a1;
float a2;
memset(&a1,0,sizeof(int));
memset(&a2,0,sizeof(float));

En el primer caso, es fácil verificar que se están pasando los tamaños correctos memset. En el segundo caso, debe revisar constantemente las secciones superior e inferior para asegurarse de que sea coherente.

vy32
fuente
44
Además, si cambia el tipo de la variable, el código que supone que el tipo tendrá que cambiar. Creo que es preferible el código correcto con la menor dependencia posible del tipo. (Estoy influenciado por un gran proyecto de conversión de 16 bits a 32 bits en el pasado).
Gort the Robot
Esto es preferible para trabajar con matrices en C donde no es común crear un typedef, pero usar cosas como char array [MAX_SIZE];
mattnz
@ vy32: Incorrecto 1: "sizeof (array) NO devuelve el tamaño de la matriz ... tamaño del puntero a la matriz" - si de arrayhecho es una matriz C, entonces sizeof(array)devuelve el número de bytes necesarios para almacenar los elementos de la matriz . Si arrayes un puntero al primer elemento de una matriz en C descompuesta, entonces su declaración es válida. Pasar una matriz C como argumento de función hace que parezca un puntero en la función: toda la información que demuestra que era una matriz, como su tamaño, se pierde. Por eso está podrido . Error 2: "las matrices en realidad no existen en C; ... simplemente azúcar sintáctica para punteros".
Johann Gerell
9

La preferencia (como siempre) es reflejar su intención lo más directamente posible.

¿Es la intención operar contra la memoria de una variable existente? Si es así, úselo sizeof(variable), ya que esto muestra lo más de cerca posible que lo que le interesa es la memoria de la variable misma.

¿Es la intención realizar algún cálculo sobre el tipo, por ejemplo, para determinar cuánta memoria debe asignarse para una nueva instancia? Si es así, úsalo sizeof(type).

Es decir, prefiero

struct foo *bar;
bar = (struct foo *) malloc(sizeof(struct foo));

encima

bar = (struct foo *) malloc(sizeof(*bar));

ya que el último caso parece que estás intentando acceder a una variable que aún no existe.

Por otro lado, prefiero

char Buffer[256];
memset(Buffer, 0, sizeof(Buffer));

encima

char Buffer[256];
memset(Buffer, 0, 256 * sizeof(char));

Como la intención es claramente llenar con cero el contenido de la variable, entonces es la variable con la que debemos operar. Intentar usar los metadatos tipo simplemente confunde las cosas, aquí.

Aidan Cully
fuente
Siempre lo hago, pero eso es una cuestión de gusto personal. Considero que void *se convierten automáticamente en otros tipos de punteros como una característica incorrecta.
Aidan Cully
3

Preferiría mucho sizeof(type)más que antes sizeof variable. A pesar de que te obliga a preocuparse más por el tipo y hacer más cambios, que ayuda a evitar errores puntero de indirección, que se encuentran entre la causa más común de errores en C .

No me preocuparía tanto por los errores en los que sizeof(type)se cambia el tipo ; cada vez que cambie el tipo de una variable, debe hacer un escaneo rápido para ver dónde se usa esa variable y si necesita cambiar alguna sizeof(type)declaración. Aunque siempre existe la posibilidad de equivocarse, tiene un riesgo mucho menor que los errores de indirección del puntero.

Una de las ventajas sizeof variablees para las matrices, pero en la práctica he descubierto que pasar matrices como pares de first / len es mucho más común (en cuyo caso solo use la longitud), además también es muy fácil obtener el tamaño incorrecto debido a decaimiento de matriz / puntero, como en este ejemplo:

ID_INLINE mat3_t::mat3_t( float src[ 3 ][ 3 ] ) {
  memcpy( mat, src, sizeof( src ) );    /* NOT the same as 3*3*sizeof(float) */
}
congusbongus
fuente
2
"Exploración rápida para ver dónde se usa esa variable ... siempre existe la posibilidad de equivocarse". Para mí, este es el mayor peligro de hacer sizeof (type), por lo que siempre hago sizeof (variable). Debido a que el resultado final de este peligro es que todo podría compilarse e incluso si comete un error, puede llevarle más de un año o fallas aleatorias y daños en el búfer antes de detectarlo. Y no pude hacer la conexión (obviamente, mi ventana de tiempo por pregunta es de aproximadamente 30 segundos) entre esta pregunta y los "errores de indirección del puntero".
DXM
@DXM Con el tamaño del operando, si es un valor, entonces es sizeof var; si se trata de un puntero que es sizeof *var. Esto también es algo en lo que la gente puede equivocarse, y el compilador lo tomará felizmente sin quejarse. Sostengo que estos errores son mucho más difíciles de detectar y son más comunes que los errores con la sizeof(type)forma.
congusbongus
1
Sus dos tipos correctos: el operador C type y sizeof están fundamentalmente rotos y nunca serán confiables, nada de lo que haga como programador puede solucionarlo.
mattnz
A medida que se etiqueta la Cpublicación, publicar el código C es más informativo que el código C ++ mat3_t::mat3_t(...).
chux - Restablece a Mónica el
0

El objetivo es eliminar la redundancia. Si usa sizeof para una operación relacionada con una variable, entonces obviamente lo reflejará en sizeof. Sí, puede equivocarse como en el primer ejemplo, pero la cura no es el tipo de uso, sino * var, que coincide correctamente con el objetivo.

Para mitigar estos problemas, es habitual usar macros, plantillas y herramientas similares capacitadas para el caso de uso en lugar de tamaño desnudo.

Vea mi macro CLEAR que se usa exclusivamente en lugar de memset desnudo. Me salvé el culo varias veces en casos de error tipográfico indirecto o cuando un contenido de estructura recogió un vector o cadena ...

Balog Pal
fuente
Macros como ese son los que dieron un mal nombre a la programación Macro de C ......
mattnz
@mattnz: muestra la mejor alternativa. Es mucho más fácil hablar cosas abstractas que crear programas que realmente funcionen
Balog Pal
1
La macro vinculada es C ++, y esta publicación está etiquetada como 'C' (son idiomas diferentes), Ada sería mi sugerencia.
mattnz
@mattnz: y el mismo tema muestra cómo aplicar de manera similar para C. Parece que se pierde completamente el punto, esa no es la implementación de contenido real, sino un uso más seguro en comparación con cosas sin procesar. Por cierto formas legibles también.
Balog Pal
0

La lógica empresarial define su elección.

  1. Si su código se refiere a una variable en particular y sin esta variable su código no tiene sentido, elija sizeof(var)

  2. Si codifica ofertas con un conjunto de variables con un tipo particular, elija sizeof(type). Por lo general, necesita esto si tiene un typedefque define muchas variables que procesa de manera diferente en función de su tipo (por ejemplo, serialización). Es posible que no sepa cuál de estas variables permanecerá en futuras versiones de código, por lo que elegir el tipo como argumento es lógicamente correcto. Incluso el cambio de tales typedefno afectará su tamaño de línea.

Riga
fuente
-1

Dependiendo del propósito sizeof (variable) podría ser la mejor solución. De esa manera, puede cambiar su tipo de variable si es necesario, sin corregir todas las entradas para el tipo. Pero de nuevo, depende de tu objetivo.

Pedro Pérez
fuente