2010-03-08 10 views
10

¿Cómo puedo obtener de manera confiable el tamaño de una matriz C-style? El método recomendado a menudo parece ser el uso de sizeof, pero no funciona en la función foo, donde se pasa en x:¿Cómo obtener confiablemente el tamaño de la matriz C-style?

#include <iostream> 

void foo(int x[]) { 
    std::cerr << (sizeof(x)/sizeof(int)); // 2 
} 

int main(){ 
    int x[] = {1,2,3,4,5}; 
    std::cerr << (sizeof(x)/sizeof(int)); // 5        
    foo(x); 
    return 0; 
} 

respuestas a this question recomienda sizeof pero no dicen que (al parecer ?) no funciona si pasa la matriz. Entonces, ¿tengo que usar un centinela? (No creo que los usuarios de mi función foo siempre se puede confiar en que poner un centinela al final. Por supuesto, podría usar std::vector, pero entonces yo no consigo el buen sintaxis abreviada {1,2,3,4,5}.)

+1

http://stackoverflow.com/questions/1810083/c-pointers-pointing-to-an-array-of-fixed-size – AnT

+0

Si los usuarios de su función no hacen lo que los documentos les dicen que hagan, eso es su problema no es tuyo Es imposible evitar que los despistados hagan un mal uso de una API. –

+0

'x' en' foo' es un puntero, en lugar de una matriz. Consulte http://stackoverflow.com/questions/232691/how-can-i-get-the-size-of-an-array-from-a-pointer-in-c, http://stackoverflow.com/questions/1975128/sizeof-a-array-in-the-c-programming-language. Además, dup: http://stackoverflow.com/questions/37538/c-how-do-i-determine-the-size-of-my-array – outis

Respuesta

15

En C, los parámetros de la matriz en C son solo indicadores para que sizeof() no funcione. O necesita pasar el tamaño como otro parámetro o usar un centinela, el que sea más apropiado para su diseño.

Algunas otras opciones:

Algunos otra información:

  • para C++, en lugar de pasar un puntero de la lista cruda, es posible que desee tener la utilización de parámetros algo que envuelve la matriz en una clase plantilla que realiza un seguimiento del tamaño de la matriz y proporciona métodos para copiar datos en la matriz de manera segura. Algo como STLSoft's array_proxy template o Boost's boost::array podría ayudar. He usado una plantilla array_proxy con buen efecto antes. Dentro de la función que utiliza el parámetro, obtiene std::vector como operaciones, pero la persona que llama de la función puede usar una simple matriz C. No hay copia de la matriz: la plantilla array_proxy se encarga de empaquetar el puntero de la matriz y el tamaño de la matriz de forma casi automática.

  • una macro para usar en C para determinar el número de elementos en una matriz (para cuando sizeof() podría ayudar - es decir, no se está tratando con un simple puntero.): Is there a standard function in C that would return the length of an array?

3

Puede pasar el tamaño, usar un centinela o incluso usar mejor std :: vector. A pesar de que std :: vector carece de listas de inicializador todavía es fácil de construir un vector con un conjunto de elementos (aunque no tan agradable)

static const int arr[] = {1,2,3,4,5}; 
vector<int> vec (arr, arr + sizeof(arr)/sizeof(arr[0])); 

La clase std :: vector también hace cometer errores mucho más difícil, que vale su peso en oro. Otra ventaja es que todo C++ debe estar familiarizado con él y la mayoría de las aplicaciones de C++ deben usar un std :: vector en lugar de una matriz C en bruto.

Como una nota rápida, C++ 0x añade Initializer lists

std::vector<int> v = {1, 2, 3, 4}; 

También puede utilizar Boost.Assign a hacer lo mismo, aunque la sintaxis es un poco más complicado.

std::vector<int> v = boost::assign::list_of(1)(2)(3)(4); 

o

std::vector<int> v; 
v += 1, 2, 3, 4; 
2

c no ofrece soporte nativo para esto. Una vez que se pasa una matriz fuera de su alcance declarado, se pierde su tamaño.

Usted puede pase el tamaño con la matriz. Incluso puedes agruparlos en una estructura para mantener el tamaño, aunque tendrás algo de gastos indirectos con eso.

1

¿Qué tal esto? ..

template <int N> 
void foo(int (&x)[N]) { 
    std::cerr << N; 
}
+0

-1: Esta es una idea horrible. – Brian

+1

@Brian: Puede o no ser una mala idea, según el contexto. ¿Por qué crees que es horrible? Es una buena idea, ya que NO COMPARARÁ si pasa un puntero. Es malo si la función es grande y se llama con muchos tamaños de matriz diferentes - generará un conjunto de funciones. Una alternativa podría ser que este foo voltee y llame a un foo_internal privado/interno (int * x, size_t length); Depende del uso. He encontrado esta técnica útil con funciones de cadena que tienden a desbordarse si no se usan de forma adecuada. Un comentario sin explicación es una idea horrible. – tony

0

Tiene que pasar el tamaño junto con la matriz, al igual que se hace en muchas funciones de biblioteca, por ejemplo strncpy(), strncmp() etc. Lo sentimos, esto es sólo la forma en que funciona en C :-).

Si lo prefiere puede desplegar su propia estructura como:

struct array { 
    int* data; 
    int size; 
}; 

y pasarlo alrededor de su código.

Por supuesto, usted todavía puede usar std::list o std::vector si quiere ser más C++ -ish.

4

Un common idiom mencionado en GNU libstdC++ documentación es la función lengthof:

template<typename T, unsigned int sz> 
inline unsigned int lengthof(T (&)[sz]) { return sz; } 

Usted puede usarlo como

int x[] = {1,2,3,4,5}; 
std::cerr << lengthof(x) << std::endl; 

Advertencia: esto va a funcionar sólo cuando la matriz no tiene decayed into a pointer.

1

Una expresión de matriz tendrá su tipo convertido implícitamente de "N-elemento matriz de T" a "puntero a T" y su valor será la dirección del primer elemento en la matriz, a menos que la expresión de matriz sea el operando de los operadores sizeof o address-of (&), o si la expresión de matriz es un literal de cadena que se usa para inicializar otra matriz en una declaración. En resumen, no puede pasar una matriz a una función como una matriz; lo que la función recibe es un valor de puntero, no un valor de matriz.

Tiene que pasar el tamaño de la matriz como un parámetro separado.

Dado que está utilizando C++, utilice vectores (o algún otro contenedor STL adecuado) en lugar de matrices de estilo C. Sí, pierdes la útil sintaxis abreviada, pero la compensación vale más que eso. Seriamente.

2

También estoy de acuerdo en que el método anterior de Corwin es muy bueno.

template <int N> 
void foo(int (&x)[N]) 
{ 
    std::cerr << N; 
} 

Yo no creo que nadie dio una muy buena razón por la que esto no es una buena idea.
en Java, por ejemplo, podemos escribir cosas como:

int numbers [] = {1, 2, 3, 4}; 
for(int i = 0; i < numbers.length(); i++) 
{ 
    System.out.println(numbers[i]+"\n"); 
} 

en C++ que sería bueno en lugar de decir

int numbers [] = {1, 2, 3, 4}; 
int size = sizeof(numbers)/sizeof(int); 
for(int i = 0; i < size; i++) 
{ 
    cout << numbers[i] << endl; 
} 

Podríamos dar un paso más e ir

template <int N> 
int size(int (&X)[N]) 
{ 
    return N; 
} 

O si eso causa problemas, supongo que podría escribir explícitamente:

template < int N > 
int size(int (&X)[N]) 
{ 
    int value = (sizeof(X)/sizeof(X[0])); 
    return value; 
} 

Entonces sólo tiene que ir en el principal:

int numbers [] = {1, 2, 3, 4}; 
for(int i = 0; i < size(numbers); i++) 
{ 
    cout << numbers[i] << endl; 
} 

tiene sentido para mí :-)

0

ya que C++ 11, hay una manera muy conveniente:

static const int array[] = { 1, 2, 3, 6 }; 
int size = (int)std::distance(std::begin(array), std::end(array))+1; 
Cuestiones relacionadas