2009-10-21 10 views

Respuesta

57

A menudo la técnica descrita en otras respuestas se encapsula en una macro para que sea más fácil para los ojos. Algo así como:

#define COUNT_OF(arr) (sizeof(arr)/sizeof(0[arr])) 

Tenga en cuenta que la macro anterior utiliza un pequeño truco de poner el nombre de la matriz en el operador de índice ('[]') en lugar del 0 - esto se hace en caso de que la macro se utiliza erróneamente en C++ código con un artículo que sobrecarga operator[](). El compilador se quejará en lugar de dar un mal resultado.

Sin embargo, también tenga en cuenta que si pasa un puntero en lugar de una matriz, la macro dará silenciosamente un resultado negativo: este es uno de los principales problemas al usar esta técnica.

Recientemente he comenzado a utilizar una versión más compleja que la que le robé a base de código de Google Cromo:

#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x]))/((size_t)(!(sizeof(x) % sizeof(0[x]))))) 

En esta versión si un puntero se pasa por error como el argumento, el compilador se quejará en algunos casos - específicamente si el tamaño del puntero no es divisible de manera uniforme por el tamaño del objeto al que apunta el puntero. En esa situación, una división por cero hará que el compilador salga de error. En realidad, al menos un compilador que he usado da una advertencia en lugar de un error. No estoy seguro de qué genera para la expresión que tiene una división por cero en ella.

Eso macro no cierra la puerta a usarlo erróneamente, pero viene lo más cerca que he visto en C. recta

Si desea una solución aún más seguro para cuando se trabaja en C++, eche un vistazo a Compile time sizeof_array without using a macro que describe un método basado en plantillas bastante complejo que Microsoft usa en winnt.h.

+3

+1 para la interesante versión de cromo. –

+1

+1 tanto para la versión de Chromium como para el pequeño truco de usar 0 [x]. –

+0

¿Puedes explicar por qué esa sintaxis, '0 [arr]', funciona como 'arr [0]'? – meguli

5
sizeof array/sizeof array[0] 
+11

Debe explicar que esto solo funciona para arreglos de tamaño fijo donde el tamaño se conoce en tiempo de compilación. –

+1

Puede acortar esto aún más usando '* array' :-) –

+0

La expresión también funciona para VLA (matrices de longitud variable) donde el tamaño se determina en tiempo de ejecución. No funciona para matrices asignadas dinámicamente (a través de 'malloc()' etc.). También falla en 'arreglos' en las listas de parámetros de funciones, pero no son matrices de todos modos. –

14

No, no lo hay.

Para matrices de tamaño constante puede utilizar el truco común Andrew mencionó, sizeof(array)/sizeof(array[0]) - pero esto sólo funciona en el ámbito de la matriz se declaró en
sizeof(array) le da el tamaño de todo el conjunto, mientras que sizeof(array[0]) le da el tamaño. del primer elemento.
Vea Michaels answer sobre cómo ajustar eso en una macro.

Para las matrices asignadas dinámicamente puede realizar un seguimiento del tamaño en un tipo integral o hacerlo terminado en 0 si es posible (es decir, asignar 1 elemento más y establecer el último elemento en 0).

+11

Tenga en cuenta que 'sizeof (array)/sizeof (array [0])' solo funciona en el mismo ámbito en que se declaró el conjunto. No podemos pasar 'array' a una función y esperamos que ese código funcione. –

+0

Por supuesto, gracias. Agregó eso. –

+0

No solo funciona para matrices de tamaño constante, sino también para matrices de longitud variable en C99. En ese caso, sin embargo, el 'sizeof' se evalúa en tiempo de ejecución. –

0

Si tiene un objeto a de tipo matriz, la cantidad de elementos en la matriz se puede expresar como sizeof a/sizeof *a. Si permitía que su objeto de matriz decayera a tipo de puntero (o solo tenía un objeto de puntero para comenzar), entonces, en general, no hay forma de determinar la cantidad de elementos en la matriz.

2

La respuesta simple, por supuesto, es no. Pero la respuesta práctica es "Necesito saber de todos modos", así que analicemos los métodos para evitar esto.

Una forma de salirse con la suya por un tiempo, como se ha mencionado acerca ya un millón de veces, es con sizeof():

int i[] = {0, 1, 2}; 
... 
size_t i_len = sizeof(i)/sizeof(i[0]); 

Esto funciona, hasta que tratan de pasar i a una función, o tomar una puntero a i. Entonces, ¿qué pasa con las soluciones más generales?

La solución general aceptada es pasar la longitud de la matriz a una función junto con la matriz. Esto lo vemos mucho en la biblioteca estándar:

void *memcpy(void *s1, void *s2, size_t n); 

copiará n bytes desde s1 a s2, que nos permite utilizar n para asegurar que nuestras memorias intermedias no rebose. Esta es una buena estrategia: tiene una sobrecarga baja, y en realidad genera un código eficiente (compárese con strcpy(), que tiene que verificar el final de la cadena y no tiene forma de "saber" cuántas iteraciones debe realizar, y deficiente). confunde strncpy(), que tiene que verificar ambos: puede ser más lento, y cualquiera podría acelerarse usando memcpy() si por alguna razón ya has calculado la longitud de la cadena).

Otro enfoque es encapsular su código en un struct. El truco común es la siguiente:

typedef struct _arr { 
    size_t len; 
    int arr[0]; 
} arr; 

Si queremos una matriz de longitud 5, hacemos esto:

arr *a = malloc(sizeof(*a) + sizeof(int) * 5); 
a->len = 5; 

Sin embargo, esto es un truco que es sólo moderadamente bien definida (C99 que permite use int arr[]) y requiere mucho trabajo. Una forma "mejor definido" de hacer esto es:

typedef struct _arr { 
    size_t len; 
    int *arr; 
} arr; 

Pero entonces nuestras asignaciones (y cancelaciones de asignación) se vuelven mucho más complicado. El beneficio de cualquiera de estos enfoques es, por supuesto, que ahora las matrices que fabriques llevarán con ellas todo su alcance. Es ligeramente menos eficiente con la memoria, pero es bastante seguro. Si elige una de estas rutas, asegúrese de escribir funciones auxiliares para que no tenga que asignar y desasignar manualmente (y trabajar con) estas estructuras.

+3

"Struct Hack" es un hack, pero no hay razón para hacerlo más hackish de lo que debe ser. Las matrices de tamaño 0 eran (y son) siempre explícitamente ilegales en C. La declaración de estructura adecuada para "struct hack" utiliza una matriz de tamaño 1. El tamaño de 'malloc' se calcula tradicionalmente como' offsetof (arr, arr) + n * sizeof (int) '(por lo que no importa qué tamaño se use en la declaración de la matriz). – AnT

+0

Son ilegales, pero muchos compiladores de C antiguos los respaldaban (por pereza, sin duda), y he visto 'arr [0]' muchas veces (con picardía). Nunca he visto el tamaño calculado con 'offsetof()' aunque es una buena manera de garantizarlo. Y no estoy de acuerdo. Hacks debería gritar en voz alta "¡Esto es un truco! ¡Mejora este código cuando tengas la oportunidad!" –

+0

Bueno, yo diría que la belleza de "struct hack" es que no viola ninguna restricción. El programa debe compilar bien. "Struct Hack" siempre se basó en la "definición" práctica del comportamiento teórico indefinido. Y, como todos sabemos, la vida a menudo nos hace confiar en eso. Por eso amamos y apreciamos ese "struct hack". Pero la versión '[0]' hace un cambio cualitativo drástico en todo eso. Introduce una violación de restricción, lo que lleva a la posible falla de compilación. Es difícil amar algo que ni siquiera compila. (Sí, sé que algunos compiladores permitieron y aún permiten arreglos '[0]'.) – AnT

3

El número de elementos de una matriz x se puede obtener:

sizeof(x)/sizeof(x[0]) 

Necesita ser consciente de que las matrices, cuando se pasa a las funciones, se degradan en punteros que hacen no llevan la información . En realidad, la información de tamaño es nunca disponible para el tiempo de ejecución ya que se calcula en tiempo de compilación, pero puede actuar como si estuviera disponible donde la matriz es visible (es decir, donde no se ha degradado).

Cuando paso arrays a una función que I necesidad de tratar como matrices, siempre me aseguro se pasan dos argumentos:

  • la longitud de la matriz; y
  • el puntero a la matriz.

Así, mientras que la matriz puede ser tratada como un conjunto donde se declaró, se trata como un puntero tamaño y en cualquier otro lugar.

que tienden a tener un código como:

#define countof(x) (sizeof(x)/sizeof(x[0])) 
: : : 
int numbers[10]; 
a = fn (countof(numbers),numbers); 

continuación fn() tendrán la información disponible a la misma.

Otro truco que he usado en el pasado (un poco más desordenado en mi opinión pero lo daré aquí para completarlo) es tener una matriz de una unión y hacer que el primer elemento tenga la longitud, algo así como:

typedef union { 
    int len; 
    float number; 
} tNumber; 
tNumber number[10]; 
: : : 
number[0].len = 5; 
a = fn (number); 

luego fn() pueden acceder a la longitud y a todos los elementos y no tiene que preocuparse por la dicotomía matriz/puntero.

Esto tiene la ventaja adicional de permitir que la longitud para variar (es decir, el número de elementos en uso, no el número de unidades asignado). Pero tiendo a no usar esto más ya que considero mejor la versión de matriz de dos argumentos (tamaño y datos).

+0

Ese truco de 'unión' es limpio, pero prefiero usar el hack' struct' (o una versión no-hack de la misma idea) . –

Cuestiones relacionadas