jkidv
En primer lugar, aquí hay un código:
int main()
{
int days[] = {1,2,3,4,5};
int *ptr = days;
printf("%u\n", sizeof(days));
printf("%u\n", sizeof(ptr));
return 0;
}
¿Hay alguna manera de averiguar el tamaño de la matriz que ptr
está apuntando (en lugar de simplemente dar su tamaño, que es de cuatro bytes en un sistema de 32 bits)?
pablo tomblin
No, no puedes. El compilador no sabe a qué apunta el puntero. Hay trucos, como terminar la matriz con un valor fuera de banda conocido y luego contar el tamaño hasta ese valor, pero eso no es usar sizeof()
.
Otro truco es el mencionado por Zan, que consiste en esconder el tamaño en algún lugar. Por ejemplo, si está asignando dinámicamente la matriz, asigne un bloque un int más grande que el que necesita, guarde el tamaño en el primer int y regrese ptr+1
como el puntero a la matriz. Cuando necesite el tamaño, disminuya el puntero y mire el valor escondido. Solo recuerde liberar todo el bloque comenzando desde el principio, y no solo la matriz.
-
Lamento haber publicado un comentario tan tarde, pero si el compilador no sabe a qué apunta el puntero, ¿cómo sabe Free cuánta memoria debe borrar? Sé que esta información se almacena internamente para funciones como el uso gratuito. Entonces mi pregunta es ¿por qué el compilador también puede hacerlo?
– viki.omega9
3 de marzo de 2013 a las 19:19
-
@viki.omega9, porque free descubre el tamaño en tiempo de ejecución. El compilador no puede saber el tamaño porque podría hacer que la matriz tenga un tamaño diferente según los factores de tiempo de ejecución (argumentos de la línea de comandos, contenido de un archivo, fase de la luna, etc.).
–Paul Tomblin
3 de marzo de 2013 a las 20:48
-
Seguimiento rápido, ¿por qué no hay una función que pueda devolver el tamaño de la forma en que lo hace gratis?
– viki.omega9
3 de marzo de 2013 a las 22:52
-
Bueno, si pudiera garantizar que la función solo se llamó con memoria mallocada y la biblioteca realiza un seguimiento de la memoria mallocada de la manera que lo he visto hacer (usando un int antes del puntero devuelto), entonces podría escribir uno. Pero si el puntero es a una matriz estática o similar, fallaría. Del mismo modo, no hay garantía de que el tamaño de la memoria malloced sea accesible para su programa.
–Paul Tomblin
3 de marzo de 2013 a las 23:11
-
@viki.omega9: Otra cosa a tener en cuenta es que el tamaño registrado por el sistema malloc/free puede no ser el tamaño que solicitó. Usted malloc 9 bytes y obtiene 16. Malloc 3K bytes y obtiene 4K. O situaciones similares.
– Zan Lince
20 de julio de 2014 a las 3:48
zan lince
La respuesta es no.”
Lo que hacen los programadores de C es almacenar el tamaño de la matriz en alguna parte. Puede ser parte de una estructura, o el programador puede hacer un poco de trampa y malloc()
más memoria de la solicitada para almacenar un valor de longitud antes del inicio de la matriz.
-
Así es como se implementan las cadenas de pascal.
– dsm
29 de enero de 2009 a las 16:44
-
¡y aparentemente las cadenas de pascal son la razón por la que Excel se ejecuta tan rápido!
– Adam Naylor
14 de julio de 2010 a las 19:48
-
@Adam: Es rápido. Lo uso en una lista de implementación de cadenas mías. Es súper rápido para la búsqueda lineal porque es: cargar el tamaño, precargar pos+tamaño, comparar el tamaño con el tamaño de búsqueda, si es igual a strncmp, pasar a la siguiente cadena, repetir. Es más rápido que la búsqueda binaria hasta unas 500 cadenas.
– Zan Lince
14 de julio de 2010 a las 19:52
Ryan
Para arreglos dinámicos (malloc o C++ nuevo) necesita almacenar el tamaño de la matriz como lo mencionaron otros o quizás construir una estructura de administrador de matrices que maneje agregar, eliminar, contar, etc. Desafortunadamente, C no hace esto tan bien como C ++, ya que básicamente tiene que construirlo para cada tipo de matriz diferente que está almacenando, lo cual es engorroso si tiene varios tipos de matrices que necesita administrar.
Para matrices estáticas, como la de su ejemplo, se usa una macro común para obtener el tamaño, pero es no recomendado ya que no verifica si el parámetro es realmente una matriz estática. Sin embargo, la macro se usa en código real, por ejemplo, en los encabezados del kernel de Linux, aunque puede ser ligeramente diferente a la siguiente:
#if !defined(ARRAY_SIZE)
#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
#endif
int main()
{
int days[] = {1,2,3,4,5};
int *ptr = days;
printf("%u\n", ARRAY_SIZE(days));
printf("%u\n", sizeof(ptr));
return 0;
}
Puede buscar en Google por razones para desconfiar de macros como esta. Ten cuidado.
Si es posible, el stdlib de C++ como vector, que es mucho más seguro y fácil de usar.
-
ARRAY_SIZE es un paradigma común utilizado por programadores prácticos en todas partes.
– SanjayaR
29 de enero de 2009 a las 17:19
-
Sí, es un paradigma común. Sin embargo, aún debe usarlo con precaución, ya que es fácil de olvidar y usarlo en una matriz dinámica.
– Ryan
29 de enero de 2009 a las 17:27
-
Sí, buen punto, pero la pregunta que se hizo fue sobre el puntero, no el de matriz estática.
–Paul Tomblin
29 de enero de 2009 a las 17:40
-
Que
ARRAY_SIZE
macro siempre funciona si su argumento es una matriz (es decir, expresión del tipo de matriz). Para su llamada “matriz dinámica”, nunca obtiene una “matriz” real (expresión del tipo de matriz). (Por supuesto, no puede, ya que los tipos de matriz incluyen su tamaño en tiempo de compilación). Solo obtiene un puntero al primer elemento. Su objeción “no verifica si el parámetro es realmente una matriz estática” no es realmente válida, ya que son diferentes ya que una es una matriz y la otra no.– nueva cuenta
28 de febrero de 2013 a las 18:52
-
Hay una función de plantilla flotando que hace lo mismo pero evitará el uso de punteros.
– Natalia Adams
23 de abril de 2013 a las 2:24
skurton
Hay una solución limpia con plantillas de C++, sin usar tamaño de(). El seguimiento obtenerTamaño() función devuelve el tamaño de cualquier matriz estática:
#include <cstddef>
template<typename T, size_t SIZE>
size_t getSize(T (&)[SIZE]) {
return SIZE;
}
Aquí hay un ejemplo con un pie estructura:
#include <cstddef>
template<typename T, size_t SIZE>
size_t getSize(T (&)[SIZE]) {
return SIZE;
}
struct foo_t {
int ball;
};
int main()
{
foo_t foos3[] = {{1},{2},{3}};
foo_t foos5[] = {{1},{2},{3},{4},{5}};
printf("%u\n", getSize(foos3));
printf("%u\n", getSize(foos5));
return 0;
}
Producción:
3
5
Como han indicado todas las respuestas correctas, no puede obtener esta información solo del valor del puntero decaído de la matriz. Si el puntero decaído es el argumento recibido por la función, entonces el tamaño de la matriz de origen debe proporcionarse de alguna otra manera para que la función llegue a conocer ese tamaño.
Aquí hay una sugerencia diferente de lo que se ha proporcionado hasta ahora, que funcionará: pase un puntero a la matriz en su lugar. Esta sugerencia es similar a las sugerencias de estilo de C++, excepto que C no admite plantillas ni referencias:
#define ARRAY_SZ 10
void foo (int (*arr)[ARRAY_SZ]) {
printf("%u\n", (unsigned)sizeof(*arr)/sizeof(**arr));
}
Pero, esta sugerencia es un poco tonta para su problema, ya que la función está definida para saber exactamente el tamaño de la matriz que se pasa (por lo tanto, hay poca necesidad de usar sizeof en la matriz). Sin embargo, lo que sí hace es ofrecer algún tipo de seguridad. Le prohibirá pasar una matriz de un tamaño no deseado.
int x[20];
int y[10];
foo(&x); /* error */
foo(&y); /* ok */
Si se supone que la función puede operar en cualquier tamaño de matriz, deberá proporcionar el tamaño a la función como información adicional.
David
Para este ejemplo específico, sí, existe, SI usa typedefs (ver más abajo). Por supuesto, si lo hace de esta manera, es mejor que use SIZEOF_DAYS, ya que sabe a qué apunta el puntero.
Si tiene un puntero (vacío *), como lo devuelve malloc() o similar, entonces, no, no hay forma de determinar a qué estructura de datos apunta el puntero y, por lo tanto, no hay forma de determinar su tamaño.
#include <stdio.h>
#define NUM_DAYS 5
typedef int days_t[ NUM_DAYS ];
#define SIZEOF_DAYS ( sizeof( days_t ) )
int main() {
days_t days;
days_t *ptr = &days;
printf( "SIZEOF_DAYS: %u\n", SIZEOF_DAYS );
printf( "sizeof(days): %u\n", sizeof(days) );
printf( "sizeof(*ptr): %u\n", sizeof(*ptr) );
printf( "sizeof(ptr): %u\n", sizeof(ptr) );
return 0;
}
Producción:
SIZEOF_DAYS: 20
sizeof(days): 20
sizeof(*ptr): 20
sizeof(ptr): 4
DigitalRoss
No existe una solución mágica. C no es un lenguaje reflexivo. Los objetos no saben automáticamente lo que son.
Pero tienes muchas opciones:
- Obviamente, agregue un parámetro
- Envuelva la llamada en una macro y agregue automáticamente un parámetro
- Usa un objeto más complejo. Defina una estructura que contenga la matriz dinámica y también el tamaño de la matriz. Luego, pase la dirección de la estructura.
-
Los objetos saben lo que son. Pero si apunta a un subobjeto, no hay forma de obtener información sobre el objeto completo o un subobjeto más grande.
–MM
29 de enero de 2020 a las 4:14
Siempre he usado paréntesis con sizeof; seguro que hace que parezca una llamada de función, pero creo que es más claro.
–Paul Tomblin
29 de enero de 2009 a las 16:44
¿Por que no? ¿Tienes algo en contra de los paréntesis superfluos? Creo que se lee un poco más fácilmente con ellos, yo mismo.
–David Thornley
29 de enero de 2009 a las 16:44
@Paul: bueno… suponiendo que el lado izquierdo de esa llamada sea un puntero a int, lo escribiría como int *ptr = malloc(4 * sizeof *ptr); que para mí es mucho más claro. Menos paréntesis para leer y llevar la constante literal al frente, como en matemáticas.
– relajarse
29 de enero de 2009 a las 17:32
@unwind: ¡no asigne una serie de punteros cuando se refiere a una serie de enteros!
–Paul Tomblin
29 de enero de 2009 a las 17:47
Aquí no hay un “puntero que apunte a una matriz”. Solo un puntero que apunta a un int.
– nueva cuenta
28 de febrero de 2013 a las 19:01