¿Cómo determino el tamaño de mi matriz en C?

Respuestas:

1262

Resumen Ejecutivo:

int a[17];
size_t n = sizeof(a)/sizeof(a[0]);

Respuesta completa:

Para determinar el tamaño de su matriz en bytes, puede usar el sizeof operador:

int a[17];
size_t n = sizeof(a);

En mi computadora, los ints tienen 4 bytes de largo, entonces n es 68.

Para determinar el número de elementos en la matriz, podemos dividir el tamaño total de la matriz por el tamaño del elemento de la matriz. Podrías hacer esto con el tipo, así:

int a[17];
size_t n = sizeof(a) / sizeof(int);

y obtener la respuesta adecuada (68/4 = 17), pero si el tipo de acambio tuvieras un error desagradable si también olvidaras cambiarlo sizeof(int).

Entonces el divisor preferido es sizeof(a[0])o el equivalente sizeof(*a), el tamaño del primer elemento de la matriz.

int a[17];
size_t n = sizeof(a) / sizeof(a[0]);

Otra ventaja es que ahora puede parametrizar fácilmente el nombre de la matriz en una macro y obtener:

#define NELEMS(x)  (sizeof(x) / sizeof((x)[0]))

int a[17];
size_t n = NELEMS(a);
Mark Harrison
fuente
66
El código generado será idéntico, ya que el compilador conoce el tipo de * int_arr en tiempo de compilación (y, por lo tanto, el valor de sizeof (* int_arr)). Será una constante, y el compilador puede optimizar en consecuencia.
Mark Harrison
10
Debería ser el caso con todos los compiladores, ya que los resultados de sizeof se definen como una constante de tiempo de compilación.
Mark Harrison
451
Importante : ¡No dejes de leer aquí, lee la siguiente respuesta! Esto solo funciona para matrices en la pila , por ejemplo, si está usando malloc () o accediendo a un parámetro de función, no tiene suerte. Vea abajo.
Markus
77
Para la programación de API de Windows en C o C ++, existe el ARRAYSIZEmakro definido en WinNT.h(que se obtiene de otros encabezados). Por lo tanto, los usuarios de WinAPI no necesitan definir su propio makro.
Lumi
17
@ Markus funciona para cualquier variable que tenga un tipo de matriz; esto no tiene que estar "en la pila". Por ej static int a[20];. Pero su comentario es útil para los lectores que pueden no darse cuenta de la diferencia entre una matriz y un puntero.
MM
808

El sizeofcamino es el correcto si se trata de matrices no recibidas como parámetros. Una matriz enviada como parámetro a una función se trata como un puntero, por sizeoflo que devolverá el tamaño del puntero, en lugar de la matriz.

Por lo tanto, las funciones internas de este método no funcionan. En su lugar, siempre pase un parámetro adicional que size_t sizeindique el número de elementos en la matriz.

Prueba:

#include <stdio.h>
#include <stdlib.h>

void printSizeOf(int intArray[]);
void printLength(int intArray[]);

int main(int argc, char* argv[])
{
    int array[] = { 0, 1, 2, 3, 4, 5, 6 };

    printf("sizeof of array: %d\n", (int) sizeof(array));
    printSizeOf(array);

    printf("Length of array: %d\n", (int)( sizeof(array) / sizeof(array[0]) ));
    printLength(array);
}

void printSizeOf(int intArray[])
{
    printf("sizeof of parameter: %d\n", (int) sizeof(intArray));
}

void printLength(int intArray[])
{
    printf("Length of parameter: %d\n", (int)( sizeof(intArray) / sizeof(intArray[0]) ));
}

Salida (en un sistema operativo Linux de 64 bits):

sizeof of array: 28
sizeof of parameter: 8
Length of array: 7
Length of parameter: 2

Salida (en un sistema operativo Windows de 32 bits):

sizeof of array: 28
sizeof of parameter: 4
Length of array: 7
Length of parameter: 1
Elideb
fuente
10
¿por qué length of parameter:2si solo se pasa un puntero al primer elemento de matriz?
Bbvarghe
16
@Bbvarghe Eso se debe a que los punteros en los sistemas de 64 bits tienen 8 bytes (sizeof (intArray)), pero los ints aún tienen (generalmente) 4 bytes de longitud (sizeof (intArray [0])).
Elideb
13
@Pacerier: no hay un código correcto; la solución habitual es pasar la longitud junto con la matriz como un argumento separado.
Jean Hominal
10
Espera, ¿no hay forma de acceder a la matriz directamente desde un puntero y ver su tamaño? Nuevo en C aquí.
sudo
77
@ Michael Trouw: puede utilizar la sintaxis de operador si se hace sentir mejor: (sizeof array / sizeof *array).
chqrlie
134

Vale la pena señalar que eso sizeofno ayuda cuando se trata de un valor de matriz que se ha descompuesto en un puntero: aunque apunta al comienzo de una matriz, para el compilador es lo mismo que un puntero a un solo elemento de esa matriz . Un puntero no "recuerda" nada más sobre la matriz que se usó para inicializarlo.

int a[10];
int* p = a;

assert(sizeof(a) / sizeof(a[0]) == 10);
assert(sizeof(p) == sizeof(int*));
assert(sizeof(*p) == sizeof(int));
Magnus Hoff
fuente
1
@ Magnus: el estándar define sizeof como el rendimiento del número de bytes en el objeto y ese sizeof (char) es siempre uno. El número de bits en un byte es específico de la implementación. Editar: ANSI C ++ sección estándar 5.3.3 Sizeof: "El operador sizeof produce el número de bytes en la representación de objeto de su operando. Sizeof (char), sizeof (char con signo) y sizeof (char sin signo) son 1; el resultado de sizeof aplicado a cualquier otro tipo fundamental está definido por la implementación ".
Skizz
Sección 1.6 El modelo de memoria C ++: "La unidad de almacenamiento fundamental en el modelo de memoria C ++ es el byte. Un byte es al menos lo suficientemente grande como para contener cualquier miembro del conjunto de caracteres de ejecución básica y está compuesto por una secuencia contigua de bits, el número de los cuales está definido por la implementación ".
Skizz
2
Recuerdo que el CRAY tenía C con char32 bits. Todo lo que dice el estándar es que se pueden representar valores enteros de 0 a 127, y su rango es al menos -127 a 127 (char está firmado) o 0 a 255 (char no está firmado).
vonbrand 01 de
55
Esta es una excelente respuesta. Quiero comentar que todas las afirmaciones anteriores se evalúan como VERDADERAS.
Javad
49

El tamaño del "truco" es la mejor manera que conozco, con un cambio pequeño pero importante (para mí, esto es una gran molestia) en el uso de paréntesis.

Como deja en claro la entrada de Wikipedia, C sizeofno es una función; Es un operador . Por lo tanto, no requiere paréntesis alrededor de su argumento, a menos que el argumento sea un nombre de tipo. Esto es fácil de recordar, ya que hace que el argumento parezca una expresión emitida, que también usa paréntesis.

Entonces: si tiene lo siguiente:

int myArray[10];

Puede encontrar la cantidad de elementos con un código como este:

size_t n = sizeof myArray / sizeof *myArray;

Eso, para mí, se lee mucho más fácil que la alternativa con paréntesis. También estoy a favor del uso del asterisco en la parte derecha de la división, ya que es más conciso que la indexación.

Por supuesto, todo esto es tiempo de compilación también, por lo que no hay necesidad de preocuparse por la división que afecta el rendimiento del programa. Utilice este formulario siempre que pueda.

Siempre es mejor usar sizeof en un objeto real cuando tiene uno, en lugar de un tipo, ya que no necesita preocuparse por cometer un error y decir el tipo incorrecto.

Por ejemplo, supongamos que tiene una función que genera algunos datos como una secuencia de bytes, por ejemplo, a través de una red. Llamemos a la función send()y hagamos que tome como argumentos un puntero al objeto a enviar y el número de bytes en el objeto. Entonces, el prototipo se convierte en:

void send(const void *object, size_t size);

Y luego debes enviar un número entero, así que codifícalo así:

int foo = 4711;
send(&foo, sizeof (int));

Ahora, ha introducido una forma sutil de dispararse en el pie, especificando el tipo de fooen dos lugares. Si uno cambia pero el otro no, el código se rompe. Por lo tanto, siempre hazlo así:

send(&foo, sizeof foo);

Ahora estás protegido. Claro, duplica el nombre de la variable, pero eso tiene una alta probabilidad de romperse de una manera que el compilador puede detectar, si la cambia.

relajarse
fuente
Por cierto, ¿son instrucciones idénticas a nivel de procesador? ¿ sizeof(int)Requiere menos instrucciones que sizeof(foo)?
Pacerier
@Pacerier: no, son idénticos. Piensa en int x = 1+1;versus int x = (1+1);. Aquí, los paréntesis son puramente estéticos.
quetzalcoatl
@Aidiakapi Eso no es cierto, considere los VLA C99.
Descansa el
@unwind Gracias, estoy corregido. Para corregir mi comentario, sizeofsiempre será constante en C ++ y C89. Con las matrices de longitud variable de C99, se puede evaluar en tiempo de ejecución.
Aidiakapi
2
sizeofpuede ser un operador, pero debe tratarse como una función de acuerdo con Linus Torvalds. Estoy de acuerdo. Lea su racional aquí: lkml.org/lkml/2012/7/11/103
Dr. Person Person II
37
int size = (&arr)[1] - arr;

Mira este enlace para una explicación

Arjun Sreedharan
fuente
77
Nitpick pequeño: el resultado de la resta del puntero tiene tipo ptrdiff_t. (Normalmente en un sistema de 64 bits, este será un tipo más grande que int). Incluso si cambia inta ptrdiff_teste código, todavía tiene un error si arrocupa más de la mitad del espacio de direcciones.
MM
2
@MM Otro pequeño detalle: Dependiendo de la arquitectura de su sistema, el espacio de direcciones no es tan grande como el tamaño del puntero en la mayoría de los sistemas. Windows, por ejemplo, limita el espacio de direcciones para aplicaciones de 64 bits a 8 TB o 44 bits. Entonces, incluso si tiene una matriz más grande que la mitad de su espacio de direcciones 4.1TB, por ejemplo, no será un error. Solo si su espacio de direcciones supera los 63 bits en esos sistemas, es posible incluso encontrar dicho error. En general, no te preocupes por eso.
Aidiakapi
1
@Aidiakapi en Linux x86 de 32 bits o en Windows con la /3Gopción de que tenga división de usuario / núcleo 3G / 1G, lo que le permite tener un tamaño de matriz de hasta el 75% del tamaño del espacio de direcciones.
Ruslan
1
Considerar foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];como variables globales. buf3[]la declaración falla ya (&buf1)[1] - buf1que no es una constante.
chux - Restablece a Mónica
2
Este es un comportamiento técnicamente indefinido ya que el estándar no permite explícitamente la desreferenciación más allá del final de una matriz (incluso si no intenta leer el valor almacenado)
MM
21

Si conoce el tipo de datos de la matriz, puede usar algo como:

int arr[] = {23, 12, 423, 43, 21, 43, 65, 76, 22};

int noofele = sizeof(arr)/sizeof(int);

O si no conoce el tipo de datos de la matriz, puede usar algo como:

noofele = sizeof(arr)/sizeof(arr[0]);

Nota: Esto solo funciona si la matriz no está definida en tiempo de ejecución (como malloc) y la matriz no se pasa en una función. En ambos casos, arr(nombre de matriz) es un puntero.

Abhitesh khatri
fuente
44
int noofele = sizeof(arr)/sizeof(int);es solo hasta la mitad mejor que la codificación int noofele = 9;. El uso sizeof(arr)mantiene la flexibilidad en caso de que cambie el tamaño de la matriz. Sin embargo, sizeof(int)necesita una actualización si el tipo de arr[]cambio. Mejor usar sizeof(arr)/sizeof(arr[0])incluso si el tipo es bien conocido. No está claro por qué usar intpara noofelevs. size_t, el tipo devuelto por sizeof().
chux - Restablece a Monica
19

La macro de la ARRAYELEMENTCOUNT(x)que todos hacen uso se evalúa incorrectamente . Esto, de manera realista, es solo una cuestión delicada, porque no puede tener expresiones que den como resultado un tipo de "matriz".

/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x[0]))

ARRAYELEMENTCOUNT(p + 1);

Realmente evalúa como:

(sizeof (p + 1) / sizeof (p + 1[0]));

Mientras

/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x)[0])

ARRAYELEMENTCOUNT(p + 1);

Evalúa correctamente a:

(sizeof (p + 1) / sizeof (p + 1)[0]);

Esto realmente no tiene mucho que ver con el tamaño de las matrices explícitamente. Acabo de notar muchos errores al no observar realmente cómo funciona el preprocesador C. Siempre envuelve el parámetro macro, no puede estar involucrada una expresión.


Esto es correcto; Mi ejemplo fue malo. Pero eso es exactamente lo que debería suceder. Como mencioné anteriormente p + 1, terminará como un tipo de puntero e invalidará toda la macro (al igual que si intentara usar la macro en una función con un parámetro de puntero).

Al final del día, en este caso particular , la falla realmente no importa (así que solo estoy perdiendo el tiempo de todos; ¡huzzah!), Porque no tienes expresiones con un tipo de 'matriz'. Pero realmente el punto sobre la evaluación del preprocesador es sutil, creo que es importante.

Peter Mortensen
fuente
2
Gracias por la explicación. La versión original produce un error en tiempo de compilación. Clang informa que "el valor suscrito no es una matriz, puntero o vector". Este comportamiento parece preferible en este caso, aunque sus comentarios sobre el orden de evaluación en macros están bien tomados.
Mark Harrison
1
No había pensado en la queja del compilador como una notificación automática de un tipo incorrecto. ¡Gracias!
3
¿Hay alguna razón para no usar (sizeof (x) / sizeof (*x))?
seriousdev
16

Para matrices multidimensionales es un poco más complicado. A menudo las personas definen constantes macro explícitas, es decir

#define g_rgDialogRows   2
#define g_rgDialogCols   7

static char const* g_rgDialog[g_rgDialogRows][g_rgDialogCols] =
{
    { " ",  " ",    " ",    " 494", " 210", " Generic Sample Dialog", " " },
    { " 1", " 330", " 174", " 88",  " ",    " OK",        " " },
};

Pero estas constantes también se pueden evaluar en tiempo de compilación con sizeof :

#define rows_of_array(name)       \
    (sizeof(name   ) / sizeof(name[0][0]) / columns_of_array(name))
#define columns_of_array(name)    \
    (sizeof(name[0]) / sizeof(name[0][0]))

static char* g_rgDialog[][7] = { /* ... */ };

assert(   rows_of_array(g_rgDialog) == 2);
assert(columns_of_array(g_rgDialog) == 7);

Tenga en cuenta que este código funciona en C y C ++. Para matrices con más de dos dimensiones use

sizeof(name[0][0][0])
sizeof(name[0][0][0][0])

etc., ad infinitum.

Andreas Spindler
fuente
15
sizeof(array) / sizeof(array[0])
Ted Percival
fuente
Dependiendo del tipo que arraytenga, no necesita usar sizeof(array) / sizeof(array[0])si arrayes una matriz de cualquiera de ellos char, unsigned charo signed char- Cita de C18,6.5.3.4 / 4: "Cuando sizeof se aplica a un operando que tiene el tipo char, unsigned char o firmado char , (o una versión calificada del mismo) el resultado es 1. " En este caso, simplemente puede hacer lo sizeof(array)que se explica en mi respuesta dedicada .
RobertS apoya a Monica Cellio el
15

Tamaño de una matriz en C:

int a[10];
size_t size_of_array = sizeof(a);      // Size of array a
int n = sizeof (a) / sizeof (a[0]);    // Number of elements in array a
size_t size_of_element = sizeof(a[0]); // Size of each element in array a                                          
                                       // Size of each element = size of type
Yogeesh HT
fuente
44
Curioso que el código utilizado size_t size_of_elementtodavía intcon int n = sizeof (a) / sizeof (a[0]); nosize_t n = sizeof (a) / sizeof (a[0]);
Chux - Restablecer Monica
1
Hola @Yogeesh HT, ¿puedes responder a la duda de chux? También tengo mucha curiosidad por saber cómo int n = sizeof (a) / sizeof (a [0]) está dando la longitud de la matriz y por qué no estamos usando size_t para la longitud de la matriz. ¿Alguien puede responder?
Cerebro
1
@Brain sizeof (a) da el tamaño de todos los elementos presentes en la matriz a sizeof (a [0]) da el tamaño de los primeros elementos. Supongamos que a = {1,2,3,4,5}; sizeof (a) = 20bytes (si sizeof (int) = 4bytes multiplica 5), ​​sizeof (a [0]) = 4bytes, entonces 20/4 = 5 es decir, sin elementos
Yogeesh HT
2
@YogeeshHT Para matrices muy grandes como char a[INT_MAX + 1u];, int ncomo se usa en, int n = sizeof (a) / sizeof (a[0]);es insuficiente (es UB). El uso size_t n = sizeof (a) / sizeof (a[0]);no incurre en este problema.
chux - Restablece a Monica
13

Aconsejaría nunca usar sizeof(incluso si se puede usar) para obtener cualquiera de los dos tamaños diferentes de una matriz, ya sea en número de elementos o en bytes, que son los dos últimos casos que muestro aquí. Para cada uno de los dos tamaños, las macros que se muestran a continuación se pueden usar para hacerlo más seguro. La razón es hacer obvia la intención del código para los mantenedores, y la diferencia con sizeof(ptr)respecto sizeof(arr)a primera vista (lo que está escrito de esta manera no es obvio), de modo que los errores son obvios para todos los que leen el código.


TL; DR:

#define ARRAY_SIZE(arr)     (sizeof(arr) / sizeof((arr)[0]) + must_be_array(arr))

#define ARRAY_SSIZE(arr)    ((ptrdiff_t)ARRAY_SIZE(arr))

#define ARRAY_BYTES(arr)    (sizeof((arr)[0]) * ARRAY_SIZE(arr))

must_be_array(arr)(definido a continuación) SE necesita como -Wsizeof-pointer-divestá con errores (a partir de abril / 2020):

#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")
#define must_be_array(a)        (                                       \
        0 * (int)sizeof(                                                \
                struct {                                                \
                        Static_assert_array(a);                         \
                        char ISO_C_forbids_a_struct_with_no_members__;  \
                }                                                       \
        )                                                               \
)

Ha habido errores importantes con respecto a este tema: https://lkml.org/lkml/2015/9/3/428

No estoy de acuerdo con la solución que proporciona Linus, que es nunca usar la notación de matriz para los parámetros de las funciones.

Me gusta la notación de matriz como documentación de que se está utilizando un puntero como matriz. Pero eso significa que se debe aplicar una solución infalible para que sea imposible escribir código con errores.

De una matriz tenemos tres tamaños que podríamos querer saber:

  • El tamaño de los elementos de la matriz.
  • El número de elementos en la matriz.
  • El tamaño en bytes que la matriz usa en la memoria

El tamaño de los elementos de la matriz.

El primero es muy simple, y no importa si estamos tratando con una matriz o un puntero, porque se hace de la misma manera.

Ejemplo de uso:

void foo(ptrdiff_t nmemb, int arr[static nmemb])
{
        qsort(arr, nmemb, sizeof(arr[0]), cmp);
}

qsort() necesita este valor como su tercer argumento.


Para los otros dos tamaños, que son el tema de la pregunta, queremos asegurarnos de que estamos tratando con una matriz, y romper la compilación si no, porque si estamos tratando con un puntero, obtendremos valores incorrectos . Cuando la compilación se interrumpe, podremos ver fácilmente que no estábamos tratando con una matriz, sino con un puntero, y solo tendremos que escribir el código con una variable o una macro que almacena el tamaño de la matriz. matriz detrás del puntero.


El número de elementos en la matriz.

Este es el más común, y muchas respuestas le han proporcionado la típica macro ARRAY_SIZE:

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

Dado que el resultado de ARRAY_SIZE se usa comúnmente con variables de tipo con signo ptrdiff_t, es bueno definir una variante con signo de esta macro:

#define ARRAY_SSIZE(arr)    ((ptrdiff_t)ARRAY_SIZE(arr))

Las matrices con más de PTRDIFF_MAXmiembros darán valores no válidos para esta versión firmada de la macro, pero al leer C17 :: 6.5.6.9, matrices como esa ya están jugando con fuego. Solo ARRAY_SIZEy size_tdebe usarse en esos casos.

Las versiones recientes de compiladores, como GCC 8, le avisarán cuando aplique esta macro a un puntero, por lo que es seguro (existen otros métodos para hacerlo seguro con compiladores más antiguos).

Funciona dividiendo el tamaño en bytes de toda la matriz por el tamaño de cada elemento.

Ejemplos de uso:

void foo(ptrdiff_t nmemb)
{
        char buf[nmemb];

        fgets(buf, ARRAY_SIZE(buf), stdin);
}

void bar(ptrdiff_t nmemb)
{
        int arr[nmemb];

        for (ptrdiff_t i = 0; i < ARRAY_SSIZE(arr); i++)
                arr[i] = i;
}

Si estas funciones no usaran matrices, sino que las obtuvieron como parámetros, el código anterior no se compilaría, por lo que sería imposible tener un error (dado que se usa una versión reciente del compilador o que se usa algún otro truco) , y necesitamos reemplazar la llamada de macro por el valor:

void foo(ptrdiff_t nmemb, char buf[nmemb])
{

        fgets(buf, nmemb, stdin);
}

void bar(ptrdiff_t nmemb, int arr[nmemb])
{

        for (ptrdiff_t i = 0; i < nmemb; i++)
                arr[i] = i;
}

El tamaño en bytes que la matriz usa en la memoria

ARRAY_SIZE se usa comúnmente como una solución al caso anterior, pero este caso rara vez se escribe de manera segura, tal vez porque es menos común.

La forma común de obtener este valor es usar sizeof(arr). El problema: el mismo que el anterior; Si tiene un puntero en lugar de una matriz, su programa se volverá loco.

La solución al problema consiste en usar la misma macro que antes, que sabemos que es segura (rompe la compilación si se aplica a un puntero):

#define ARRAY_BYTES(arr)        (sizeof((arr)[0]) * ARRAY_SIZE(arr))

Cómo funciona es muy simple: deshace la división que lo ARRAY_SIZEhace, por lo que después de las cancelaciones matemáticas terminas con solo una sizeof(arr), pero con la seguridad adicional de la ARRAY_SIZEconstrucción.

Ejemplo de uso:

void foo(ptrdiff_t nmemb)
{
        int arr[nmemb];

        memset(arr, 0, ARRAY_BYTES(arr));
}

memset() necesita este valor como su tercer argumento.

Como antes, si la matriz se recibe como un parámetro (un puntero), no se compilará, y tendremos que reemplazar la llamada de macro por el valor:

void foo(ptrdiff_t nmemb, int arr[nmemb])
{

        memset(arr, 0, sizeof(arr[0]) * nmemb);
}

Actualización (23 / abr / 2020): -Wsizeof-pointer-divtiene errores :

Hoy descubrí que la nueva advertencia en GCC solo funciona si la macro se define en un encabezado que no es un encabezado del sistema. Si define la macro en un encabezado que está instalado en su sistema (generalmente /usr/local/include/o /usr/include/) ( #include <foo.h>), el compilador NO emitirá una advertencia (probé GCC 9.3.0).

Entonces tenemos #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))y queremos hacerlo seguro. Necesitaremos C11 _Static_assert()y algunas extensiones de GCC: declaraciones y declaraciones en expresiones , __builtin_types_compatible_p :

#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")

#define ARRAY_SIZE(arr)         (                                       \
{                                                                       \
        Static_assert_array(arr);                                       \
        sizeof(arr) / sizeof((arr)[0]);                                \
}                                                                       \
)

Ahora ARRAY_SIZE()es completamente seguro y, por lo tanto, todos sus derivados estarán seguros.


Actualización: libbsd proporciona __arraycount():

Libbsd ofrece la macro __arraycount()en <sys/cdefs.h>, lo que no es seguro porque carece de un par de paréntesis, pero podemos añadir esos paréntesis, a nosotros mismos, y por lo tanto ni siquiera necesita para escribir la división en nuestra cabecera (¿por qué habríamos de duplicar el código que ya existe? ) Esa macro se define en un encabezado del sistema, por lo que si la usamos nos vemos obligados a usar las macros anteriores.

#include <sys/cdefs.h>


#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")

#define ARRAY_SIZE(arr)         (                                       \
{                                                                       \
        Static_assert_array(arr);                                       \
        __arraycount((arr));                                            \
}                                                                       \
)

#define ARRAY_SSIZE(arr)        ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr)        (sizeof((arr)[0]) * ARRAY_SIZE(arr))

Algunos sistemas proporcionan nitems()en su <sys/param.h>lugar, y algunos sistemas proporcionan ambos. Debe verificar su sistema y usar el que tiene, y tal vez usar algunos condicionales de preprocesador para portabilidad y soporte para ambos.


Actualización: Permita que la macro se use en el alcance del archivo:

Desafortunadamente, la ({})extensión gcc no se puede usar en el alcance del archivo. Para poder usar la macro en el ámbito del archivo, la aserción estática debe estar dentro sizeof(struct {}). Luego, multiplíquelo por 0para no afectar el resultado. Una conversión a (int)podría ser buena para simular una función que devuelve (int)0(en este caso no es necesario, pero luego es reutilizable para otras cosas).

#include <sys/cdefs.h>


#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")
#define must_be_array(a)        (                                       \
        0 * (int)sizeof(                                                \
                struct {                                                \
                        Static_assert_array(a);                         \
                        char ISO_C_forbids_a_struct_with_no_members__;  \
                }                                                       \
        )                                                               \
)

#define ARRAY_SIZE(arr)         (__arraycount((arr)) + must_be_array(arr))
#define ARRAY_SSIZE(arr)        ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr)        (sizeof((arr)[0]) * ARRAY_SIZE(arr))
Cacahuete Frito
fuente
3
¿Te importaría explicar por qué el voto negativo? Este muestra una solución a una construcción inseguro y común ( sizeof(arr)) que no se muestra en otra parte ARRAY_BYTES(arr).
Cacahuete Frito
2
ARRAY_SIZE es lo suficientemente común como para usarse libremente, y ARRAY_BYTES es muy explícito en su nombre, debe definirse junto a ARRAY_SIZE para que un usuario pueda ver ambos fácilmente, y por su uso, no creo que nadie que lea el código tenga dudas sobre qué lo hace. Lo que quise decir es no usar un simple sizeof, sino usar estas construcciones en su lugar; si tiene ganas de escribir estas construcciones cada vez, es probable que cometa un error (muy común si copia pegar, y también muy común si las escribe cada vez porque tienen muchos paréntesis) ...
Cacahuete Frito
3
..., así que estoy en la conclusión principal: una sola sizeofes claramente insegura (las razones están en la respuesta), y no usar macros sino usar las construcciones que proporcioné, cada vez es aún más inseguro, por lo que la única forma de hacerlo es macros
Cacahuete Frito
3
@ MarkHarrison Sé la diferencia entre punteros y matrices. Pero ha habido ocasiones en que tuve una función que luego refactoricé en pequeñas funciones, y lo que primero fue una matriz, luego fue un puntero, y ese es un punto en el que si olvidas cambiar el tamaño de, lo atornillas, y es fácil no verlo uno de esos.
Cacahuete Frito
3
@hyde También sé que la diferencia no significa que todos sepan la diferencia, y ¿por qué no usar algo que básicamente elimine el 100% de esos errores? Ese error casi llegó a Linux; llegó a Linus, lo que significa que pasó mucho escrutinio, y también significa que existe la posibilidad de que el mismo error haya llegado a Linux en otra parte del núcleo, como él dice.
Cacahuete Frito
11

"has introducido una forma sutil de dispararte en el pie"

Las matrices C 'nativas' no almacenan su tamaño. Por lo tanto, se recomienda guardar la longitud de la matriz en una variable / constante separada y pasarla cada vez que pase la matriz, es decir:

#define MY_ARRAY_LENGTH   15
int myArray[MY_ARRAY_LENGTH];

Siempre DEBE evitar las matrices nativas (a menos que no pueda, en cuyo caso, cuidar su pie) Si está escribiendo C ++, use el contenedor 'vector' de STL . "En comparación con las matrices, proporcionan casi el mismo rendimiento", ¡y son mucho más útiles!

// vector is a template, the <int> means it is a vector of ints
vector<int> numbers;  

// push_back() puts a new value at the end (or back) of the vector
for (int i = 0; i < 10; i++)
    numbers.push_back(i);

// Determine the size of the array
cout << numbers.size();

Ver: http://www.cplusplus.com/reference/stl/vector/

Ohad
fuente
He leído que la forma correcta de declarar constantes enteras en C es usar una enumdeclaración.
Raffi Khatchadourian
La pregunta es sobre C, no C ++. Entonces no hay STL.
Cacahuete Frito
11
#define SIZE_OF_ARRAY(_array) (sizeof(_array) / sizeof(_array[0]))
Andy Nugent
fuente
66
Tenga en cuenta que esto solo funciona para matrices reales, no para punteros que apuntan a matrices.
David Schwartz
5

Si realmente desea hacer esto para pasar su matriz, sugiero implementar una estructura para almacenar un puntero al tipo del que desea una matriz y un número entero que represente el tamaño de la matriz. Entonces puedes pasar eso a tus funciones. Simplemente asigne el valor de la variable de matriz (puntero al primer elemento) a ese puntero. Luego puede ir Array.arr[i]a obtener el elemento i-ésimo y usarlo Array.sizepara obtener el número de elementos en la matriz.

Incluí un código para ti. No es muy útil, pero podría ampliarlo con más funciones. Sin embargo, para ser sincero, si estas son las cosas que desea, debe dejar de usar C y usar otro lenguaje con estas funciones integradas.

/* Absolutely no one should use this...
   By the time you're done implementing it you'll wish you just passed around
   an array and size to your functions */
/* This is a static implementation. You can get a dynamic implementation and 
   cut out the array in main by using the stdlib memory allocation methods,
   but it will work much slower since it will store your array on the heap */

#include <stdio.h>
#include <string.h>
/*
#include "MyTypeArray.h"
*/
/* MyTypeArray.h 
#ifndef MYTYPE_ARRAY
#define MYTYPE_ARRAY
*/
typedef struct MyType
{
   int age;
   char name[20];
} MyType;
typedef struct MyTypeArray
{
   int size;
   MyType *arr;
} MyTypeArray;

MyType new_MyType(int age, char *name);
MyTypeArray newMyTypeArray(int size, MyType *first);
/*
#endif
End MyTypeArray.h */

/* MyTypeArray.c */
MyType new_MyType(int age, char *name)
{
   MyType d;
   d.age = age;
   strcpy(d.name, name);
   return d;
}

MyTypeArray new_MyTypeArray(int size, MyType *first)
{
   MyTypeArray d;
   d.size = size;
   d.arr = first;
   return d;
}
/* End MyTypeArray.c */


void print_MyType_names(MyTypeArray d)
{
   int i;
   for (i = 0; i < d.size; i++)
   {
      printf("Name: %s, Age: %d\n", d.arr[i].name, d.arr[i].age);
   }
}

int main()
{
   /* First create an array on the stack to store our elements in.
      Note we could create an empty array with a size instead and
      set the elements later. */
   MyType arr[] = {new_MyType(10, "Sam"), new_MyType(3, "Baxter")};
   /* Now create a "MyTypeArray" which will use the array we just
      created internally. Really it will just store the value of the pointer
      "arr". Here we are manually setting the size. You can use the sizeof
      trick here instead if you're sure it will work with your compiler. */
   MyTypeArray array = new_MyTypeArray(2, arr);
   /* MyTypeArray array = new_MyTypeArray(sizeof(arr)/sizeof(arr[0]), arr); */
   print_MyType_names(array);
   return 0;
}
Joel Dentici
fuente
1
No se puede votar el código que lo hace strcpy(d.name, name);sin manejar el desbordamiento.
chux - Restablece a Monica
4

La mejor manera es guardar esta información, por ejemplo, en una estructura:

typedef struct {
     int *array;
     int elements;
} list_s;

Implemente todas las funciones necesarias, como crear, destruir, verificar la igualdad y todo lo que necesite. Es más fácil pasar como parámetro.

Paulo Pinheiro
fuente
66
Cualquier razón de int elementsfrente size_t elements?
chux - Restablece a Monica
3

La función sizeofdevuelve el número de bytes que utiliza su matriz en la memoria. Si desea calcular el número de elementos en su matriz, debe dividir ese número con el sizeoftipo de variable de la matriz. Digamos que int array[10];si el entero de tipo variable en su computadora es de 32 bits (o 4 bytes), para obtener el tamaño de su matriz, debe hacer lo siguiente:

int array[10];
int sizeOfArray = sizeof(array)/sizeof(int);
Keivan
fuente
2

Puedes usar el &operador. Aquí está el código fuente:

#include<stdio.h>
#include<stdlib.h>
int main(){

    int a[10];

    int *p; 

    printf("%p\n", (void *)a); 
    printf("%p\n", (void *)(&a+1));
    printf("---- diff----\n");
    printf("%zu\n", sizeof(a[0]));
    printf("The size of array a is %zu\n", ((char *)(&a+1)-(char *)a)/(sizeof(a[0])));


    return 0;
};

Aquí está la salida de muestra

1549216672
1549216712
---- diff----
4
The size of array a is 10
Shih-En Chou
fuente
77
No voté en contra, pero esto es como golpear un clavo con un ladrillo porque no notaste un martillo a tu lado. Además, las personas tienden a fruncir el ceño al usar variables no inicializadas ... pero aquí supongo que sirve a su propósito lo suficientemente bien.
Dmitri
2
@Dmitri no se accede a variables no inicializadas aquí
MM
1
Hmmm La sustracción del puntero conduce a ptrdiff_t. sizeof()resultados en size_t. C no define cuál es más ancho o más alto / mismo rango. Por lo tanto, el tipo de cociente ((char *)(&a+1)-(char *)a)/(sizeof(a[0]))no es seguro size_ty, por lo tanto, imprimir con él zpuede conducir a UB. Simplemente usar printf("The size of array a is %zu\n", sizeof a/sizeof a[0]);es suficiente.
chux - Restablece a Monica
1
(char *)(&a+1)-(char *)ano es una constante y puede calcularse en tiempo de ejecución, incluso con un tamaño fijo a[10]. sizeof(a)/sizeof(a[0])se realiza constantemente en tiempo de compilación en este caso.
chux - Restablece a Monica
1

La respuesta más simple:

#include <stdio.h>

int main(void) {

    int a[] = {2,3,4,5,4,5,6,78,9,91,435,4,5,76,7,34};//for Example only
    int size;

    size = sizeof(a)/sizeof(a[0]);//Method

    printf ("size = %d",size);
    return 0;
}
Jency
fuente
1

Una solución más elegante será

size_t size = sizeof(a) / sizeof(*a);
Azatik1000
fuente
0

Además de las respuestas ya proporcionadas, quiero señalar un caso especial mediante el uso de

sizeof(a) / sizeof (a[0])

Si aes una matriz de char, unsigned charo signed charno necesita usar sizeofdos veces, ya sizeofque siempre resulta una expresión con un operando de estos tipos 1.

Cita de C18,6.5.3.4 / 4:

" Cuando sizeofse aplica a un operando que tiene tipo char, unsigned charo signed char, (o una versión calificada de la misma) el resultado es 1".

Por sizeof(a) / sizeof (a[0])lo tanto, sería equivalente a NUMBER OF ARRAY ELEMENTS / 1si aes una matriz de tipo char, unsigned charo signed char. La división a través de 1 es redundante.

En este caso, simplemente puede abreviar y hacer:

sizeof(a)

Por ejemplo:

char a[10];
size_t length = sizeof(a);

Si desea una prueba, aquí hay un enlace a GodBolt .


No obstante, la división mantiene la seguridad, si el tipo cambia significativamente (aunque estos casos son raros).

RobertS apoya a Monica Cellio
fuente
1
Probablemente prefiera seguir aplicando una macro con la división, porque el tipo puede cambiar en el futuro (aunque tal vez sea poco probable), y la división se conoce en el momento de la compilación, por lo que el compilador la optimizará (si no lo hace, cambie tu compilador).
Cacahuete Frito
1
@CacahueteFrito Sí, mientras tanto, también he pensado en eso. Lo tomé como una nota al margen de la respuesta. Gracias.
RobertS apoya a Monica Cellio el
-1

Nota: Este puede darle un comportamiento indefinido como lo señala MM en el comentario.

int a[10];
int size = (*(&a+1)-a) ;

Para más detalles ver aquí y también aquí .

Pygirl
fuente
2
Este es un comportamiento técnicamente indefinido; el *operador no se puede aplicar a un puntero pasado-fin
MM
3
"comportamiento indefinido" significa que el Estándar C no define el comportamiento. Si lo intentas en tu programa, entonces puede pasar cualquier cosa
MM
@MM, ¿estás diciendo que *(&a+1) - a;es diferente al (&a)[1] - a;anterior, no ambos *(&a+1)y (&a)[1]cuentan como 1 más allá del final?
QuentinUK
@QuentinUK sus dos expresiones son las mismas, x[y]se define como*(x + (y))
MM
@ MM, creo que sí. Pero la otra respuesta, por Arjun Sreedharan, tiene 38 flechas hacia arriba y esta tiene -1. Y la respuesta de Arjun Sreedharan no menciona el comportamiento indefinido.
QuentinUK