Regresar matriz en una función

Tengo una matriz int arr[5] que se pasa a una función fillarr(int arr[]) :

 int fillarr(int arr[]) { for(...); return arr; } 
  1. ¿Cómo puedo devolver esa matriz?
  2. ¿Cómo lo usaré, digamos que devolví un puntero sobre cómo voy a acceder a él?

En este caso, la variable de la matriz arr puede también tratarse como un puntero al comienzo del bloque de la matriz en la memoria, mediante una conversión implícita. Esta syntax que estás usando:

 int fillarr(int arr[]) 

Es algo así como azúcar sintáctica. Realmente podría reemplazarlo con esto y aún así funcionaría:

 int fillarr(int* arr) 

Entonces, en el mismo sentido, lo que desea devolver de su función es en realidad un puntero al primer elemento de la matriz:

 int* fillarr(int arr[]) 

Y todavía podrá usarlo como lo haría con una matriz normal:

 int main() { int y[10]; int *a = fillarr(y); cout << a[0] << endl; } 

Las funciones de C ++ no pueden devolver matrices de estilo C por valor. Lo más parecido es devolver un puntero. Además, un tipo de matriz en la lista de argumentos simplemente se convierte en un puntero.

 int *fillarr( int arr[] ) { // arr "decays" to type int * return arr; } 

Puede mejorarlo usando una matriz de referencias para el argumento y el retorno, que previene la descomposición:

 int ( &fillarr( int (&arr)[5] ) )[5] { // no decay; argument must be size 5 return arr; } 

Con Boost o C ++ 11, el paso por referencia es solo opcional y la syntax es menos alucinante:

 array< int, 5 > &fillarr( array< int, 5 > &arr ) { return arr; // "array" being boost::array or std::array } 

La plantilla de array simplemente genera una struct contiene una matriz de estilo C, por lo que puede aplicar la semántica orientada a objetos pero conservando la simplicidad original de la matriz.

$ 8.3.5 / 8 estados-

“Las funciones no deben tener un tipo de retorno de tipo matriz o función, aunque pueden tener un tipo de retorno de tipo puntero o referencia a tales cosas. No habrá conjuntos de funciones, aunque puede haber matrices de punteros a funciones”.

 int (&fn1(int (&arr)[5]))[5]{ // declare fn1 as returning refernce to array return arr; } int *fn2(int arr[]){ // declare fn2 as returning pointer to array return arr; } int main(){ int buf[5]; fn1(buf); fn2(buf); } 

En C ++ 11, puede devolver std::array .

 #include  using namespace std; array fillarr(int arr[]) { array arr2; for(int i=0; i<5; ++i) { arr2[i]=arr[i]*2; } return arr2; } 

la respuesta puede depender un poco de cómo planea usar esa función. Para obtener la respuesta más simple, decidamos que en lugar de una matriz, lo que realmente quiere es un vector. Los vectores son agradables porque el aspecto de todo el mundo es aburrido, los valores normales que puede almacenar en punteros regulares. Veremos otras opciones y por qué las quiere después:

 std::vector fillarr( std::vector arr ) { // do something return arr; } 

Esto hará exactamente lo que espera que haga. Lo bueno es que std::vector se ocupa de asegurarse de que todo se maneje de forma limpia. El inconveniente es que esto copia una gran cantidad de datos, si su matriz es grande. De hecho, copia cada elemento de la matriz dos veces. primero copia el vector para que la función pueda usarlo como parámetro. luego lo copia nuevamente para devolverlo a la persona que llama. Si puede manejar el manejo del vector usted mismo, puede hacer las cosas un poco más fácilmente. (puede copiarlo por tercera vez si la persona que llama necesita almacenarlo en una variable de algún tipo para hacer más cálculos)

Parece que lo que realmente intenta hacer es completar una colección. si no tiene una razón específica para devolver una nueva instancia de una colección, entonces no. podemos hacerlo así

 void fillarr(std::vector & arr) { // modify arr // don't return anything } 

de esta forma se obtiene una referencia a la matriz pasada a la función, no a una copia privada de la misma. cualquier cambio que realice en el parámetro es visto por la persona que llama. Podría devolver una referencia si lo desea, pero eso no es realmente una gran idea, ya que de alguna manera implica que está obteniendo algo diferente de lo que pasó.

Si realmente necesita una nueva instancia de la colección, pero desea evitar tenerla en la stack (y toda la copia que eso conlleva), debe crear algún tipo de contrato sobre cómo se maneja esa instancia. La forma más fácil de hacerlo es usar un puntero inteligente, que mantiene la instancia referenciada mientras el usuario la tenga. Se va limpiamente si se sale del scope. Eso se vería así.

 std::auto_ptr > fillarr( const std::vector & arr) { std::auto_ptr > myArr(new std::vector); // do stuff with arr and *myArr return myArr; } 

En su mayor parte, el uso de *myArr funciona de forma idéntica al uso de un vector simple de vainilla. Este ejemplo también modifica la lista de parámetros al agregar la palabra clave const . Ahora obtienes una referencia sin copiarla, pero no puedes modificarla, por lo que la persona que llama sabe que será la misma que antes de que la función llegara a ella.

Todo esto es genial, pero el c ++ idiomático rara vez funciona con colecciones en general. Más normalmente, usarás iteradores sobre esas colecciones. eso se vería más como esto

 template  Iterator fillarr(Iterator arrStart, Iterator arrEnd) { Iterator arrIter = arrStart; for(;arrIter <= arrEnd; arrIter++) ;// do something return arrStart; } 

Usarlo parece un poco extraño si no estás acostumbrado a ver este estilo.

 vector arr; vector::iterator foo = fillarr(arr.begin(), arr.end()); 

foo ahora 'señala' el comienzo del arr modificado.

Lo que es realmente bueno de esto es que funciona igual de bien en vectores como en arreglos C simples y muchos otros tipos de colección, por ejemplo

 int arr[100]; int *foo = fillarr(arr, arr+100); 

Que ahora se parece muchísimo a los ejemplos de punteros simples que se dan en otras partes de esta pregunta.

Esta:

 int fillarr(int arr[]) 

en realidad se trata de la misma manera que:

 int fillarr(int *arr) 

Ahora, si realmente desea devolver una matriz, puede cambiar esa línea para

 int * fillarr(int arr[]){ // do something to arr return arr; } 

Realmente no está devolviendo una matriz. está devolviendo un puntero al comienzo de la dirección de la matriz.

Pero recuerda cuando pasas en el conjunto, solo estás pasando un puntero. Entonces, cuando modifica los datos de la matriz, en realidad está modificando los datos a los que apunta el puntero. Por lo tanto, antes de pasar en la matriz, debe darse cuenta de que ya tiene en el exterior el resultado modificado.

p.ej

 int fillarr(int arr[]){ array[0] = 10; array[1] = 5; } int main(int argc, char* argv[]){ int arr[] = { 1,2,3,4,5 }; // arr[0] == 1 // arr[1] == 2 etc int result = fillarr(arr); // arr[0] == 10 // arr[1] == 5 return 0; } 

Sugiero que considere la posibilidad de ponerle una longitud a su función fillarr como esta.

 int * fillarr(int arr[], int length) 

De esta forma, puede usar la longitud para llenar la matriz de su longitud sin importar de qué se trate.

Para usarlo correctamente Haz algo como esto:

 int * fillarr(int arr[], int length){ for (int i = 0; i < length; ++i){ // arr[i] = ? // do what you want to do here } return arr; } // then where you want to use it. int arr[5]; int *arr2; arr2 = fillarr(arr, 5); // at this point, arr & arr2 are basically the same, just slightly // different types. You can cast arr to a (char*) and it'll be the same. 

Si todo lo que quiere hacer es establecer la matriz en algunos valores predeterminados, considere usar la función incorporada de memset.

algo así como: memset ((int *) & arr, 5, sizeof (int));

Aunque estoy en el tema sin embargo. Usted dice que está usando C ++. Eche un vistazo al uso de vectores stl. Es probable que tu código sea más robusto.

Hay muchos tutoriales. Aquí hay uno que le da una idea de cómo usarlos. http://www.yolinux.com/TUTORIALS/LinuxTutorialC++STL.html

para devolver una matriz desde una función, definamos esa matriz en una estructura; Por lo tanto, se ve algo como esto

 struct Marks{ int list[5]; } 

Ahora vamos a crear variables de la estructura tipo.

 typedef struct Marks marks; marks marks_list; 

Podemos pasar array a una función de la siguiente manera y asignarle valor:

 void setMarks(int marks_array[]){ for(int i=0;i 

También podemos devolver la matriz. Para devolver el conjunto, el tipo de retorno de la función debe ser del tipo de estructura, es decir, marcas. Esto es porque en realidad estamos pasando la estructura que contiene la matriz. Entonces, el código final puede verse así.

 marks getMarks(){ return marks_list; } 

Esta es una pregunta bastante antigua, pero voy a poner mis 2 centavos ya que hay muchas respuestas, pero ninguna muestra todos los métodos posibles de una manera clara y concisa (no estoy seguro de la parte concisa, ya que esto tiene un poco fuera de control, TL; DR 😉).

Supongo que OP desea devolver la matriz que se transfirió sin copiar como medio para pasarla directamente a la persona que llama para pasarla a otra función y hacer que el código se vea más bonito.

Sin embargo, usar una matriz como esta es dejar que se convierta en un puntero y hacer que el comstackdor lo trate como una matriz. Esto puede dar lugar a errores sutiles si pasa en una matriz como, con la función esperando que tendrá 5 elementos, pero la persona que llama realmente pasa en otro número.

Hay algunas maneras en que puedes manejar esto mejor. Pase en std::vector o std::array (no estoy seguro de si std::array existía en 2010 cuando se formuló la pregunta). A continuación, puede pasar el objeto como referencia sin copiar / mover el objeto.

 std::array& fillarr(std::array& arr) { // (before c++11) for(auto it = arr.begin(); it != arr.end(); ++it) { /* do stuff */ } // Note the following are for c++11 and higher. They will work for all // the other examples below except for the stuff after the Edit. // (c++11 and up) for(auto it = std::begin(arr); it != std::end(arr); ++it) { /* do stuff */ } // range for loop (c++11 and up) for(auto& element : arr) { /* do stuff */ } return arr; } std::vector& fillarr(std::vector& arr) { for(auto it = arr.begin(); it != arr.end(); ++it) { /* do stuff */ } return arr; } 

Sin embargo, si insistes en jugar con arrays en C, utiliza una plantilla que mantendrá la información de la cantidad de elementos en la matriz.

 template  int(&fillarr(int(&arr)[N]))[N] { // N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0]) for(int* it = arr; it != arr + N; ++it) { /* do stuff */ } return arr; } 

Excepto, eso se ve feo y súper difícil de leer. Ahora uso algo para ayudar con lo que no existía en 2010, que también uso para los indicadores de función:

 template  using type_t = T; template  type_t fillarr(type_t arr) { // N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0]) for(int* it = arr; it != arr + N; ++it) { /* do stuff */ } return arr; } 

Esto mueve el tipo donde uno esperaría que fuera, haciéndolo mucho más legible. Por supuesto, usar una plantilla es superfluo si no va a usar nada más que 5 elementos, por lo que puede, por supuesto, codificarlo con dificultad:

 type_t fillarr(type_t arr) { // Prefer using the compiler to figure out how many elements there are // as it reduces the number of locations where you have to change if needed. for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it) { /* do stuff */ } return arr; } 

Como dije, mi truco type_t<> no habría funcionado en el momento en que se hizo esta pregunta. Lo mejor que podías haber esperado en ese entonces era usar un tipo en una estructura:

 template struct type { typedef T type; }; typename type::type fillarr(typename type::type arr) { // Prefer using the compiler to figure out how many elements there are // as it reduces the number of locations where you have to change if needed. for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it) { /* do stuff */ } return arr; } 

Lo cual comienza a verse muy feo otra vez, pero al menos es aún más legible, aunque el typename puede haber sido opcional en aquel entonces según el comstackdor, lo que resulta en:

 type::type fillarr(type::type arr) { // Prefer using the compiler to figure out how many elements there are // as it reduces the number of locations where you have to change if needed. for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it) { /* do stuff */ } return arr; } 

Y luego, por supuesto, podrías haber especificado un tipo específico, en lugar de usar mi ayudante.

 typedef int(&array5)[5]; array5 fillarr(array5 arr) { // Prefer using the compiler to figure out how many elements there are // as it reduces the number of locations where you have to change if needed. for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it) { /* do stuff */ } return arr; } 

En aquel entonces, las funciones gratuitas std::begin() y std::end() no existían, aunque podrían haberse implementado fácilmente. Esto habría permitido iterar sobre la matriz de una manera más segura ya que tienen sentido en una matriz C, pero no en un puntero.

En cuanto al acceso al conjunto, puede pasarlo a otra función que tome el mismo tipo de parámetro o crear un alias (lo que no tendría mucho sentido ya que tiene el original en ese ámbito). Acceder a una referencia de matriz es como acceder a la matriz original.

 void other_function(type_t x) { /* do something else */ } void fn() { int array[5]; other_function(fillarr(array)); } 

o

 void fn() { int array[5]; auto& array2 = fillarr(array); // alias. But why bother. int forth_entry = array[4]; int forth_entry2 = array2[4]; // same value as forth_entry } 

Para resumir, es mejor no permitir que una matriz se descomponga en un puntero si tiene la intención de iterar sobre ella. Es simplemente una mala idea, ya que evita que el comstackdor lo proteja de dispararse en el pie y hace que su código sea más difícil de leer. Siempre intente y ayude al comstackdor a ayudarle manteniendo los tipos el mayor tiempo posible a menos que tenga una muy buena razón para no hacerlo.

Editar

Ah, y para completar, puedes permitir que se degrade a un puntero, pero esto desacopla la matriz del número de elementos que contiene. Esto se hace mucho en C / C ++ y generalmente se mitiga al pasar la cantidad de elementos en la matriz. Sin embargo, el comstackdor no puede ayudarlo si comete un error y pasa el valor incorrecto a la cantidad de elementos.

 // separate size value int* fillarr(int* arr, size_t size) { for(int* it = arr; it != arr + size; ++it) { /* do stuff */ } return arr; } 

En lugar de pasar el tamaño, puede pasar el puntero final, que apunta a uno más allá del final de su matriz. Esto es útil ya que se trata de algo que está más cerca de los algoritmos std, que toman un puntero de inicio y final, pero lo que usted devuelve ahora es solo algo que debe recordar.

 // separate end pointer int* fillarr(int* arr, int* end) { for(int* it = arr; it != end; ++it) { /* do stuff */ } return arr; } 

Alternativamente, puede documentar que esta función solo tomará 5 elementos y espera que el usuario de su función no haga nada estúpido.

 // I document that this function will ONLY take 5 elements and // return the same array of 5 elements. If you pass in anything // else, may nazal demons exit thine nose! int* fillarr(int* arr) { for(int* it = arr; it != arr + 5; ++it) { /* do stuff */ } return arr; } 

Tenga en cuenta que el valor de retorno ha perdido su tipo original y se degrada a un puntero. Debido a esto, ahora está solo para asegurarse de no sobrepasar el conjunto.

Podría pasar un std::pair , que puede usar para comenzar y terminar y pasarlo, pero luego realmente deja de verse como una matriz.

 std::pair fillarr(std::pair arr) { for(int* it = arr.first; it != arr.second; ++it) { /* do stuff */ } return arr; // if you change arr, then return the original arr value. } void fn() { int array[5]; auto array2 = fillarr(std::make_pair(&array[0], &array[5])); // Can be done, but you have the original array in scope, so why bother. int fourth_element = array2.first[4]; } 

o

 void other_function(std::pair array) { // Can be done, but you have the original array in scope, so why bother. int fourth_element = array2.first[4]; } void fn() { int array[5]; other_function(fillarr(std::make_pair(&array[0], &array[5]))); } 

Bastante gracioso, esto es muy similar a cómo funciona std::initializer_list (c ++ 11), pero no funcionan en este contexto.

la forma más sencilla de hacer esto es devolverlo por referencia, incluso si no escribe el símbolo ‘&’, se devuelve automáticamente por referencia

  void fillarr(int arr[5]) { for(...); } 
 int *fillarr(int arr[]) 

Todavía puedes usar el resultado como

 int *returned_array = fillarr(some_other_array); if(returned_array[0] == 3) do_important_cool_stuff(); 
 template using ARR_REF = T (&)[N]; template  ARR_REF ArraySizeHelper(ARR_REF arr); #define arraysize(arr) sizeof(ArraySizeHelper(arr)) 

Fuente: https://www.tutorialspoint.com/cplusplus/cpp_return_arrays_from_functions.htm

C ++ no permite devolver una matriz completa como argumento de una función. Sin embargo, puede devolver un puntero a una matriz especificando el nombre de la matriz sin un índice.

  1. Si desea devolver una matriz de dimensión única desde una función, debería declarar una función que devuelve un puntero como en el siguiente ejemplo:
 int * myFunction() { . . . } 
  1. C ++ no aboga por devolver la dirección de una variable local al exterior de la función, por lo que tendría que definir la variable local como variable estática.

Aplicando estas reglas en la pregunta actual, podemos escribir el progtwig de la siguiente manera:

 # include  using namespace std; int * fillarr( ); int main () { int *p; p = fillarr(); for ( int i = 0; i < 5; i++ ) cout << "p[" << i << "] : "<< *(p + i) << endl; return 0; } int * fillarr( ) { static int arr[5]; for (int i = 0; i < 5; ++i) arr[i] = i; return arr; } 

El resultado será:

 p[0]=0 p[1]=1 p[2]=2 p[3]=3 p[4]=4 

y que hay con:

 int (*func()) { int *f = new int[10] {1,2,3}; return f; } int fa[10] = { 0 }; auto func2() -> int (*) [10] { return &fa; } 

Simplemente defina un tipo [] como valor de retorno, como:

  private string[] functionReturnValueArray(string one, string two) { string[] x = {one, two}; x[0] = "a"; x[1] = "b"; return x; } 

. . . Llamada de función:

 string[] y; y = functionReturnValueArray(stringOne, stringTwo)