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

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

Es decir, ¿la cantidad de elementos que puede contener la matriz?

preguntado el 01 de septiembre de 08 a las 07:09

22 Respuestas

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 longitud, por lo que 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 obtenga la respuesta adecuada (68/4 = 17), pero si el tipo de a cambiado, tendrías un error desagradable si olvidaste cambiar el sizeof(int) también.

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);

Respondido 27 Abr '20, 22:04

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

Debería ser el caso de todos los compiladores, ya que los resultados de sizeof se definen como una constante en tiempo de compilación. - Mark Harrison

Importante:: ¡No dejes de leer aquí, lee la siguiente respuesta! Esto solo funciona para matrices en el montón, por ejemplo, si está usando malloc () o accediendo a un parámetro de función, no tiene suerte. Vea abajo. - Markus

Para la programación de API de Windows en C o C ++, existe el ARRAYSIZE makro definido en WinNT.h (que es atraído por otros encabezados). Por lo tanto, los usuarios de WinAPI no necesitan definir su propio makro. - Ilustración

@Markus funciona para cualquier variable que tenga un tipo de matriz; esto no tiene que estar "en la pila". P.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

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

Por lo tanto, dentro de las funciones, este método no funciona. En su lugar, pase siempre un parámetro adicional size_t size indicando 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

Respondido el 26 de junio de 15 a las 01:06

por que es length of parameter:2 si solo se pasa un puntero al primer elemento de la matriz? - Bbvarghe

@Bbvarghe Eso es porque los punteros en los sistemas de 64 bits tienen 8 bytes (sizeof (intArray)), pero los ints todavía tienen (normalmente) 4 bytes de longitud (sizeof (intArray [0])). - Elideb

@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

Espera, ¿no hay forma de acceder a la matriz directamente desde un puntero y ver su tamaño? Nuevo en C aquí. - sudo

@Michael Trouw: puedes usar la sintaxis del operador si te hace sentir mejor: (sizeof array / sizeof *array). - chqrlie

Vale la pena señalar que sizeof no ayuda cuando se trata de un valor de matriz que se ha reducido a un puntero: aunque apunta al inicio 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 utilizó 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));

Respondido 06 Oct 14, 10:10

@ Magnus: El estándar define sizeof como la producción de la cantidad de bytes en el objeto y que sizeof (char) es siempre uno. El número de bits en un byte es específico de la implementación. Editar: sección 5.3.3 del estándar ANSI C ++ Sizeof: "El operador sizeof produce el número de bytes en la representación del objeto de su operando. [...] sizeof (char), sizeof (carácter firmado) y sizeof (carácter sin firmar) 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ásico y está compuesto por una secuencia contigua de bits, el número de los cuales está definido por la implementación ". - Skizz

Recuerdo que el CRAY tenía C con char de 32 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 de -127 a 127 (el carácter está firmado) o de 0 a 255 (el carácter no tiene signo). - vonbrand

Esta es una excelente respuesta. Quiero comentar que todas las afirmaciones anteriores se evalúan como VERDADERAS. - Javad

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

Como deja en claro la entrada de Wikipedia, las C sizeof no es una función; se trata de 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 de conversión, 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 entre paréntesis. También prefiero el 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 también es tiempo de compilación, por lo que no hay necesidad de preocuparse de que la división afecte 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 en un tipo, ya que entonces no necesita preocuparse por cometer un error e indicar el tipo incorrecto.

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

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

Y luego necesitas enviar un número entero, así que lo codificas así:

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

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

send(&foo, sizeof foo);

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

Respondido 15 Oct 08, 14:10

Por cierto, ¿son instrucciones idénticas a nivel de procesador? Lo hace sizeof(int) requieren instrucciones menores que sizeof(foo)? - Pacerier

@Pacerier: no, son idénticos. Pensar en int x = 1+1; frente a int x = (1+1);. Aquí, los paréntesis son puramente absolutamente estéticos. - quetzalcoatl

@Aidiakapi Eso no es cierto, considere los VLA de C99. - relajarse

sizeof puede ser un operador, pero debe tratarse como una función según Linus Torvalds. Estoy de acuerdo. Lea su racional aquí: lkml.org/lkml/2012/7/11/103 - Tan apesta

¿Por qué la omisión de paréntesis debería hacerla más legible? sizeof myArray / sizeof *myArray; puede significar sizeof(myArray / sizeof *myArray); por ejemplo. Sé que no tendría sentido, pero es mejor ser explícito en mi humilde opinión. - Eric Duminil

int size = (&arr)[1] - arr;

Más info: este link para una explicación

Respondido el 10 de enero de 14 a las 12:01

Pequeño detalle: el resultado de la resta del puntero tiene tipo ptrdiff_t. (Por lo general, en un sistema de 64 bits, este será un tipo más grande que int). Incluso si cambias int a ptrdiff_t en este código, todavía tiene un error si arr ocupa más de la mitad del espacio de direcciones. - MM

@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 excede los 63 bits en esos sistemas, es posible incluso encontrar tal error. En general, no se preocupe por eso. - Aidiakapi

@Aidiakapi en Linux x32 de 86 bits o en Windows con /3G La opción tiene una división de kernel / usuario 3G / 1G, que le permite tener un tamaño de matrices de hasta el 75% del tamaño del espacio de direcciones. - Ruslan

Imagine foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1]; como variables globales. buf3[] declaración falla como (&buf1)[1] - buf1 no es una constante. - chux - Reincorporar a Monica

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

Puede usar el operador sizeof pero no funcionará para funciones porque tomará la referencia del puntero, puede hacer lo siguiente para encontrar la longitud de una matriz:

len = sizeof(arr)/sizeof(arr[0])

Código encontrado originalmente aquí: Programa en C para encontrar el número de elementos en una matriz

respondido 18 nov., 17:14

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 la matriz) es un puntero.

Respondido el 08 de Septiembre de 14 a las 13:09

int noofele = sizeof(arr)/sizeof(int); es solo la mitad de camino mejor que codificar int noofele = 9;. Utilizando sizeof(arr) mantiene la flexibilidad en caso de que cambie el tamaño de la matriz. Aún 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 int para noofele vs size_t, el tipo devuelto por sizeof(). - chux - Reincorporar a Monica

Aconsejaría no usar nunca 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 utilizar para hacerlo más seguro. La razón es hacer obvia la intención del código a los mantenedores y la diferencia sizeof(ptr) desde sizeof(arr) a primera vista (que escrito de esta manera no es obvio), por lo que los errores son obvios para todos los que lean 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) + must_be_array(arr))

#define ARRAY_SBYTES(arr)   ((ssize_t)ARRAY_BYTES(arr))

must_be_array(arr) (definido a continuación) ES necesario como -Wsizeof-pointer-div tiene errores (a abril / 2020):

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

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 quizás queramos saber:

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

El tamaño de los elementos de la matriz.

El primero es muy simple y no importa si se trata de 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 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, de lo contrario, romper la compilación, porque si estamos tratando con un puntero, obtendremos valores incorrectos. . Cuando la compilación esté rota, podremos ver fácilmente que no estamos 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 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 macro típica ARRAY_SIZE:

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

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

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

Matrices con más de PTRDIFF_MAX los miembros 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_SIZE y los size_t debe utilizarse en esos casos.

Las versiones recientes de compiladores, como GCC 8, le advertirá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 obtuvieran 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 usa la matriz 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 con el anterior; si tiene un puntero en lugar de una matriz, su programa se volverá loco.

La solución al problema implica 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))

Dado que el resultado de ARRAY_BYTES a veces se compara con la salida de funciones que devuelven ssize_t, es bueno definir una variante firmada de esta macro:

#define ARRAY_SBYTES(arr)   ((ssize_t)ARRAY_BYTES(arr))

Su funcionamiento es muy simple: deshace la división que ARRAY_SIZE lo hace, por lo que después de cancelaciones matemáticas terminas con solo una sizeof(arr), pero con la seguridad añadida de la ARRAY_SIZE construcción.

Ejemplo de uso:

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

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

memset() necesita este valor como tercer argumento.

Como antes, si la matriz se recibe como parámetro (un puntero), no se compilará y tendremos que reemplazar la llamada a la 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-div tiene errores:

Hoy descubrí que la nueva advertencia en GCC solo funciona si la macro está definida 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/ or /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 quiero que sea seguro. Necesitaremos C11 _Static_assert() y algunas extensiones 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(arr)           (!is_same_type((arr), &(arr)[0]))
#define Static_assert_array(arr) _Static_assert(is_array(arr), "Not a `[]` !")

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

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


Actualización: libbsd proporciona __arraycount():

Libbsd proporciona la macro __arraycount() in <sys/cdefs.h>, que no es seguro porque carece de un par de paréntesis, pero podemos agregar esos paréntesis nosotros mismos y, por lo tanto, ni siquiera necesitamos escribir la división en nuestro encabezado (¿por qué duplicaríamos el código que ya existe?). Esa macro está definida en un encabezado del sistema, por lo que si la usamos nos vemos obligados a usar las macros anteriores.

#include <stddef.h>
#include <sys/cdefs.h>
#include <sys/types.h>


#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(arr)           (!is_same_type((arr), &(arr)[0]))
#define Static_assert_array(arr) _Static_assert(is_array(arr), "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))
#define ARRAY_SBYTES(arr)       ((ssize_t)ARRAY_BYTES(arr))

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


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

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

Además, la definición de ARRAY_BYTES() se puede simplificar un poco.

#include <stddef.h>
#include <sys/cdefs.h>
#include <sys/types.h>


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

#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) + must_be_array(arr))
#define ARRAY_SBYTES(arr)       ((ssize_t)ARRAY_BYTES(arr))

Notas

Este código hace uso de las siguientes extensiones, que son completamente necesarias, y su presencia es absolutamente necesaria para lograr la seguridad. Si su compilador no los tiene, o algunos similares, entonces no puede lograr este nivel de seguridad.

También utilizo la siguiente función C11. Sin embargo, su ausencia mediante el uso de un estándar más antiguo se puede superar con algunos trucos sucios (ver, por ejemplo: Que es ":-!!" en código C?).

También utilizo la siguiente extensión, pero hay una forma estándar en C de hacer lo mismo.

Respondido el 24 de Septiembre de 20 a las 17:09

¿Le importaría explicar por qué el voto negativo? Esto muestra una solución a una construcción común e insegura (sizeof(arr)) que no se muestra en otra parte: ARRAY_BYTES(arr). - ALX

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, pero use 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 y pega, y también muy común si las escribe cada vez porque tienen muchos paréntesis) ... - ALX

..., así que me mantengo en la conclusión principal: una sola sizeof es claramente inseguro (las razones están en la respuesta), y no usar macros pero usar las construcciones que proporcioné, cada vez, es aún más inseguro, por lo que la única forma de hacerlo son las macros. - ALX

@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 arruinas y es fácil no ver uno de esos. - ALX

@hyde Además, que 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 lo que también significa que existe la posibilidad de que el mismo error haya llegado a Linux en otra parte del kernel, como él dice. - ALX

La macro ARRAYELEMENTCOUNT(x) que todo el mundo está utilizando evalúa incorrectamente. Esto, de manera realista, es solo un asunto delicado, porque no puede tener expresiones que resulten en un tipo 'matriz'.

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

ARRAYELEMENTCOUNT(p + 1);

Actualmente 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 por no observar realmente cómo funciona el preprocesador de C. Siempre ajusta el parámetro de macro, no una expresión en la que pueda estar involucrada.


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 (como si intentara usar la macro en una función con un parámetro de puntero).

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

respondido 09 mar '17, 07:03

Gracias por la explicación. La versión original da como resultado un error en tiempo de compilación. Clang informa que "el valor subindicado no es una matriz, un puntero o un vector". Este parece un comportamiento preferible en este caso, aunque sus comentarios sobre el orden de evaluación en las macros están bien tomados. - Mark Harrison

No había pensado en la queja del compilador como una notificación automática de un tipo incorrecto. ¡Gracias! - usuario2379628

¿Hay alguna razón para no usar (sizeof (x) / sizeof (*x))? - serio

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 tamaño de:

#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, utilice

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

etc., ad infinitum.

Respondido el 08 de Septiembre de 16 a las 13:09

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

respondido 09 mar '17, 07:03

Curioso ese código usado size_t size_of_element aún int con int n = sizeof (a) / sizeof (a[0]); y no size_t n = sizeof (a) / sizeof (a[0]); - chux - Reincorporar a Monica

Hola @Yogeesh HT, ¿podrías responder 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

@Brain sizeof (a) da sizeof de todos los elementos presentes en la matriz a sizeof (a [0]) da sizeof de los primeros elementos. Supongamos que a = {1}; sizeof (a) = 1,2,3,4,5bytes (si sizeof (int) = 20bytes multiplica 4), ​​sizeof (a [5]) = 0bytes, entonces 4/20 = 4 es decir, no de elementos - Yogeesh HT

@YogeeshHT Para matrices muy grandes como char a[INT_MAX + 1u];, int n como se usa en int n = sizeof (a) / sizeof (a[0]); es insuficiente (es UB). Utilizando size_t n = sizeof (a) / sizeof (a[0]); no incurre en este problema. - chux - Reincorporar a Monica

sizeof(array) / sizeof(array[0])

Respondido el 01 de Septiembre de 08 a las 10:09

Depende del tipo array tiene, no necesitas usar sizeof(array) / sizeof(array[0]) if array es una matriz de char, unsigned char or 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 sizeof(array) como se explica en mi dedicado https://www.youtube.com/watch?v=xB-eutXNUMXJtA&feature=youtu.be. - RobertS apoya a Monica Cellio

"has introducido una forma sutil de dispararte a ti mismo en el pie"

Las matrices "nativas" de C 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, tenga cuidado). Si está escribiendo C ++, use el STLcontenedor 's' vector '. "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/

Respondido el 08 de Septiembre de 14 a las 13:09

He leído que la forma correcta de declarar constantes enteras en C es usar un enum declaración. - Raffi Khatchadourian

La pregunta es sobre C, no sobre C ++. Entonces no STL. - ALX

#define SIZE_OF_ARRAY(_array) (sizeof(_array) / sizeof(_array[0]))

Respondido el 12 de Septiembre de 16 a las 17:09

Tenga en cuenta que esto solo funciona para matrices reales, no punteros que apunten a matrices. - David Schwartz

Si realmente desea hacer esto para pasar su matriz, le 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. Entonces puedes irte Array.arr[i] para obtener el elemento i-ésimo y usar Array.size para obtener el número de elementos de la matriz.

Incluí un código para ti. No es muy útil, pero podría ampliarlo con más funciones. Sin embargo, para ser honesto, si estas son las cosas que desea, debe dejar de usar C y usar otro lenguaje con estas características 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;
}

Respondido el 13 de junio de 13 a las 18:06

No se puede votar el código que lo hace strcpy(d.name, name); sin manejo de desbordamiento. - chux - Reincorporar a Monica

La mejor forma 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 demás que necesite. Es más fácil pasarlo como parámetro.

Respondido 17 Feb 16, 17:02

Cualquier motivo para int elements vs size_t elements? - chux - Reincorporar a Monica

La función sizeof devuelve 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 sizeof tipo de variable de la matriz. Digamos int array[10];, si el tipo de variable entero 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);

Respondido 20 ago 18, 18:08

Puede utilizar 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

Respondido 06 Oct 14, 06:10

No voté en contra, pero esto es como golpear un clavo con un ladrillo porque no notaste un martillo a tu lado. Además, la gente tiende a desaprobar el uso de variables no inicializadas ... pero aquí supongo que sirve bastante bien para su propósito. - Dmitri

@Dmitri no se accede a variables no inicializadas aquí - MM

Mmm. La resta de puntero conduce a ptrdiff_t. sizeof() resultados en size_t. C hace no defina cuál es más amplio o más alto / mismo rango. Entonces el tipo de cociente ((char *)(&a+1)-(char *)a)/(sizeof(a[0])) ciertamente no es size_t y así imprimir con z puede conducir a UB. Simplemente usando printf("The size of array a is %zu\n", sizeof a/sizeof a[0]); es suficiente. - chux - Reincorporar a Monica

(char *)(&a+1)-(char *)a no es una constante y se puede calcular en tiempo de ejecución, incluso con un tamaño fijo a[10]. sizeof(a)/sizeof(a[0]) es una constante realizada en tiempo de compilación en este caso. - chux - Reincorporar a Monica

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;
}

Respondido 08 ago 19, 11:08

Una solución más elegante será

size_t size = sizeof(a) / sizeof(*a);

Respondido el 28 de enero de 20 a las 13:01

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

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

If a es una matriz de char, unsigned char or signed char no necesitas usar sizeof dos veces desde un sizeof expresión con un operando de estos tipos siempre resultan en 1.

Cita de C18,6.5.3.4 / 4:

"Cuando te sizeof se aplica a un operando que tiene tipo char, unsigned chary signed char, (o una versión calificada del mismo) el resultado es 1."

Por lo tanto, sizeof(a) / sizeof (a[0]) sería equivalente a NUMBER OF ARRAY ELEMENTS / 1 if a es una matriz de tipo char, unsigned char or signed char. La división entre 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).

Respondido 23 Abr '20, 12:04

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 su compilador). - ALX

@CacahueteFrito Sí, yo también he pensado en eso mientras tanto. Lo tomé como una nota al margen de la respuesta. Gracias. - RobertS apoya a Monica Cellio

Aconsejaría nunca ser tan astuto. Ni siquiera agrega ninguna mejora de rendimiento, ya que la división se realiza en tiempo de compilación. - ALX

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

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

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

respondido 09 mar '20, 08:03

Este es un comportamiento técnicamente indefinido; la * El operador no se puede aplicar a un puntero más allá del final - MM

"Comportamiento indefinido" significa que el Estándar C no define el comportamiento. Si lo prueba en su programa, puede suceder cualquier cosa: MM

@MM estás diciendo *(&a+1) - a; es diferente de (&a)[1] - a; arriba, no ambos *(&a+1) y los (&a)[1] contar como 1 pasado el final? - QuentinUK

@QuentinUK tus dos expresiones son iguales, x[y] se define como *(x + (y)) - MM

@MM Eso pensé. Pero la otra respuesta, de Arjun Sreedharan, tiene 38 flechas hacia arriba y esta tiene -1. Y la respuesta de Arjun Sreedharan no menciona un comportamiento indefinido. - QuentinUK

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.