2009-01-14 103 views
16

En C++, las matrices no se pueden pasar simplemente como parámetros. Es decir, si puedo crear una función de este modo:Pasar una matriz como un parámetro de función en C++

void doSomething(char charArray[]) 
{ 
    // if I want the array size 
    int size = sizeof(charArray); 
    // NO GOOD, will always get 4 (as in 4 bytes in the pointer) 
} 

no tengo manera de saber qué tan grande es la matriz, ya que sólo tengo un puntero a la matriz.

¿De qué manera tengo, sin cambiar la firma del método, para obtener el tamaño de la matriz e iterar sobre sus datos?


EDIT: simplemente una adición con respecto a la solución. Si la matriz de caracteres, en concreto, se ha inicializado como tan:

char charArray[] = "i am a string"; 

entonces el \0 ya se adjunta al final de la matriz. En este caso, la respuesta (marcada como aceptada) funciona de la caja, por así decirlo.

+2

En realidad, aquí hay un malentendido bastante grande. La sintaxis en su ejemplo no tiene sentido: la función toma una cantidad desconocida de datos en la pila. ignora la sintaxis original y le da a la función la firma 'void doSomething (char * charArray)'. No se conserva ninguna de la matriz si la llamas con 'char array [10]; doSomething (array);'. Ver http: //c-faq.com/aryptr/aryptrparam.html – kfsone

Respuesta

22

Sin cambiar la firma? Agregar un elemento centinela. Para las matrices de caracteres específicamente, podría ser el '\0' de terminación nula que se utiliza para cadenas de C estándar.

void doSomething(char charArray[]) 
{ 
    char* p = charArray; 
    for (; *p != '\0'; ++p) 
    { 
     // if '\0' happens to be valid data for your app, 
     // then you can (maybe) use some other value as 
     // sentinel 
    } 
    int arraySize = p - charArray; 

    // now we know the array size, so we can do some thing 
} 

Por supuesto, su matriz no puede contener el elemento centinela como contenido. Para otros tipos de matrices (es decir, no carbonizadas), podría ser cualquier valor que no sea legal. Si no existe tal valor, entonces este método no funciona.

Además, esto requiere cooperación por parte del llamante. Realmente debe asegurarse de que la persona que llama reserva un conjunto de elementos arraySize + 1, y siempre establece el elemento centinela.

Sin embargo, si realmente no puede cambiar la firma, sus opciones son bastante limitadas.

+0

Ejemplo de código, por favor? :) –

+0

Sí, todavía estaba escribiendo :) – Reunanen

+0

Muy buena solución, funcionó como un encanto. ¡Gracias! –

4

si está terminado en nulo, strlen() funcionaría.

+1

Sí, esta es la respuesta perfecta para las matrices de caracteres como en la pregunta, pero realmente no funcionaría para otros tipos de datos. – Reunanen

+0

Aparte del hecho de que es perfectamente legal escribir 'doSomething (NULL);'. El ** compilador ** cambia la huella de OP a 'char * charArray' :) http://c-faq.com/aryptr/aryptrparam.html – kfsone

2

No puede determinar el tamaño de charArray solo. Esa información no se pasa automáticamente a la función.

Por supuesto, si se trata de una cadena terminada en nulo puede usar strlen(), ¡pero probablemente ya lo haya considerado!

Considere pasar un parámetro std::vector<char> &, o un par de punteros, o un puntero más un parámetro de tamaño.

+0

Creo que Yuval está limitado a la firma de la función. – Calyth

+0

@finnw Eso es porque no pasa la matriz en absoluto, solo le pasa un puntero. – kfsone

2

Esto es realmente más C que C++, en C++ probablemente prefiera usar un std :: vector. Sin embargo, en C no hay manera de saber el tamaño de una matriz. La compilación le permitirá hacer un tamaño de si el conjunto se declaró en el ámbito actual, y solo si se declaró explícitamente con un tamaño (EDIT: y "con un tamaño", quiero decir que fue declarado con un tamaño entero o inicializado en la declaración, en lugar de pasarse como un parámetro, gracias por el voto a la baja).

La solución común en C es pasar un segundo parámetro que describe la cantidad de elementos en la matriz.

EDIT:
Lo sentimos, se perdió la parte acerca de no querer cambiar la firma del método. Entonces no hay solución, excepto como otros lo describen, si hay algunos datos que no están permitidos dentro de la matriz, se puede usar como un terminador (0 en C-strings, -1 también es bastante común, pero depende de su real de tipo de datos, suponiendo que la matriz de caracteres es hipotético)

+1

-1. (a) Las matrices declaradas sin tamaños infieren sus tamaños de sus inicializadores y se puede acceder a este tamaño usando sizeof().(b) Usar una plantilla de función en su lugar (según la respuesta de Josh Kelley) * * le permite inferir el tamaño de una matriz pasada a una función. –

1

para que una función para saber el número de elementos de una matriz que se ha pasado a él, usted debe hacer una de dos cosas:

  1. Pase un parámetro de tamaño
  2. Ponga la información de tamaño en la matriz de alguna manera.

Puede hacer esto último en varias formas:

  • por terminado con un nulo o algún otro centinela que no va a ocurrir en datos normales.
  • tienda cuenta el artículo en la primera entrada si la matriz tiene un número
  • tienda de un puntero a la última entrada si la matriz contiene punteros
+0

Incluso el compilador acepta que necesita cambiar la huella digital ... http://c-faq.com/aryptr/aryptrparam.html – kfsone

35

Use plantillas. Esto técnicamente no se ajusta a sus criterios, porque cambia la firma, pero no es necesario modificar el código de llamada.

void doSomething(char charArray[], size_t size) 
{ 
    // do stuff here 
} 

template<size_t N> 
inline void doSomething(char (&charArray)[N]) 
{ 
    doSomething(charArray, N); 
} 

Esta técnica es utilizada por Microsoft de Secure CRT functions y por la plantilla array_proxy clase de STLSoft.

5

En realidad solía ser una solución bastante común para pasar la longitud en el primer elemento de la matriz. Este tipo de estructura a menudo se llama BSTR (para "cadena BÁSICA"), aunque esto también denota tipos diferentes (pero similares).

La ventaja sobre la solución aceptada es que la determinación de la longitud con un centinela es lenta para cadenas grandes. La desventaja es, obviamente, que este es un truco de bajo nivel que no respeta ni los tipos ni la estructura.

En la forma que se muestra a continuación, también solo funciona para cadenas de longitud < = 255. Sin embargo, esto puede ampliarse fácilmente almacenando la longitud en más de un byte.

void doSomething(char* charArray) 
{ 
    // Cast unnecessary but I prefer explicit type conversions. 
    std::size_t length = static_cast<std::size_t>(static_cast<unsigned char>(charArray[0])); 
    // … do something. 
} 
+3

tiene un pequeño problema debido a la extraña manipulación de C++ en C y de C. si está firmado, los valores> 127 podrían dar como resultado valores enormes de size_t (wrap-around) cuando se convierten a size_t. puede solucionarlo convirtiendo a char sin signo primero: static_cast ((char sin signo) charArray [0]); –

+0

@litb: Bien notado. –

5

En general cuando se trabaja con C o de bajo nivel C++, es posible considerar el reciclaje de su cerebro nunca que no escribe parámetros de matriz a una función, debido a que el compilador de C siempre será tratarlos como punteros de todos modos. En esencia, al escribir esos corchetes se está engañando a sí mismo al pensar que se está pasando una matriz real, completa con información de tamaño. En realidad, en C solo puedes pasar punteros. La función

void foo(char a[]) 
{ 
    // Do something... 
} 

es, desde el punto de vista del compilador C, exactamente equivalente a:

void foo(char * a) 
{ 
    // Do something 
} 

y obviamente ese puntero nekkid carbón no contiene información de longitud.

Si está atascado en una esquina y no puede cambiar la firma de la función, considere usar un prefijo de longitud como se sugirió anteriormente. Un truco no portátil pero compatibles consiste en especificar la longitud de la matriz en un campo size_t situado antes la matriz, algo como esto:

void foo(char * a) 
{ 
    int cplusplus_len = reinterpret_cast<std::size_t *>(a)[-1]; 
    int c_len = ((size_t *)a)[-1]; 
} 

Obviamente la persona que llama tiene que crear las matrices de la forma adecuada antes de pasarlos a foo

No hace falta decir que este es un hack horrible, pero este truco puede salir de problemas en un apuro.

+0

¿No es ese comportamiento no declarado? –

-1

Está garantizado para recibir 4 en una PC de 32 bits y esa es la respuesta correcta. debido a la razón explicada here y here. La respuesta corta es que en realidad está probando el tamaño de un puntero en lugar de una matriz, porque "la matriz se convierte implícitamente o se descompone en un puntero. El puntero, por desgracia, no almacena la dimensión de la matriz; Incluso te digo que la variable en cuestión es una matriz ".

Ahora que está utilizando C++, boost::array es una mejor opción que las matrices en bruto. Como es un objeto, no perderá la información de la dimensión ahora.

-2

Creo que se puede hacer esto:

size_t size = sizeof(array)/sizeof(array[0]); 

PS: Creo que el título de este tema no es correcto, también.

+2

No, todas las declaraciones en el OP son correctas. El gran problema es que en la definición 'void doSomething (char charArray []) {/*...*/}', el parámetro 'charArray' no es realmente una matriz. – aschepler

-3

Dude puede tener una variable global para almacenar el tamaño de la matriz que será accesible durante todo el programa. Al menos puede pasar el tamaño de la matriz de la función main() a la variable global y ni siquiera tendrá que cambiar la firma del método, ya que el tamaño estará disponible globalmente.

Por favor, véase el ejemplo:

#include<...> 
using namespace std; 

int size; //global variable 

//your code 

void doSomething(char charArray[]) 
{ 
    //size available 

} 
0

tratar de usar strlen (charArray); utilizando el archivo de encabezado cstring. Esto producirá la cantidad de caracteres, incluidos espacios, hasta que llegue al cierre ".

Cuestiones relacionadas