Devolviendo una matriz usando C

11 minutos de lectura

avatar de usuario
usuario1506919

Soy relativamente nuevo en C y necesito ayuda con los métodos que se ocupan de las matrices. Viniendo de la programación Java, estoy acostumbrado a poder decir int [] method()para devolver una matriz. Sin embargo, descubrí que con C tienes que usar punteros para matrices cuando los devuelves. Siendo un nuevo programador, realmente no entiendo esto en absoluto, incluso con los muchos foros que he revisado.

Básicamente, estoy tratando de escribir un método que devuelva una matriz de caracteres en C. Proporcionaré el método (llamémoslo returnArray) con una matriz. Creará una nueva matriz a partir de la matriz anterior y le devolverá un puntero. Solo necesito ayuda sobre cómo comenzar esto y cómo leer el puntero una vez que se envía fuera de la matriz. Cualquier ayuda para explicar esto es apreciada.

Formato de código propuesto para la función de retorno de matriz

char *returnArray(char array []){
 char returned [10];
 //methods to pull values from array, interpret them, and then create new array
 return &(returned[0]); //is this correct?
} 

Llamador de la función

int main(){
 int i=0;
 char array []={1,0,0,0,0,1,1};
 char arrayCount=0;
 char* returnedArray = returnArray(&arrayCount); ///is this correct?
 for (i=0; i<10;i++)
  printf(%d, ",", returnedArray[i]);  //is this correctly formatted?
}

Todavía no he probado esto porque mi compilador de C no funciona en este momento, pero me gustaría resolverlo.

  • ¿La matriz de retorno tiene un tamaño conocido como se indica en su ejemplo de código? El único otro problema que veo además de los problemas de pila mencionados en las respuestas es que si su matriz de retorno tiene un tamaño indeterminado, dada la forma en que funcionan los punteros/matrices en C, no sabrá qué tan grande es.

    – extraño mundo libre

    25 de julio de 2012 a las 18:56

  • Sí, sé el tamaño de la matriz entrante en todo momento. El tamaño de la matriz de entrada y salida no cambiará.

    – usuario1506919

    25 de julio de 2012 a las 19:01

  • El desarrollo del lenguaje C* – bell-labs.com/usr/dmr/www/chist.html

    – x4444

    31 de diciembre de 2017 a las 7:45

El tratamiento de C de las matrices es muy diferente al de Java, y tendrá que ajustar su forma de pensar en consecuencia. Las matrices en C no son objetos de primera clase (es decir, una expresión de matriz no conserva su “condición de matriz” en la mayoría de los contextos). En C, una expresión de tipo “arreglo de elementos N de T” se convertirá implícitamente (“decay”) en una expresión de tipo “puntero a T“, excepto cuando la expresión de matriz es un operando de la sizeof o unario & operadores, o si la expresión de matriz es un literal de cadena que se utiliza para inicializar otra matriz en una declaración.

Entre otras cosas, esto significa que no puede pasar una expresión de matriz a una función y recibirla como un tipo de matriz; la función en realidad recibe un tipo de puntero:

void foo(char *a, size_t asize)
{
  // do something with a
}

int bar(void)
{
  char str[6] = "Hello";
  foo(str, sizeof str);
}

en la llamada a foola expresion str se convierte de tipo char [6] para char *por lo que el primer parámetro de foo se declara char *a en vez de char a[6]. En sizeof strya que la expresión de matriz es un operando de la sizeof operador, no se convierte en un tipo de puntero, por lo que obtiene la cantidad de bytes en la matriz (6).

Si tu eres De Verdad interesado, puede leer el libro de Dennis Ritchie El desarrollo del lenguaje C para entender de dónde viene este tratamiento.

El resultado es que las funciones no pueden devolver tipos de matriz, lo cual está bien ya que las expresiones de matriz tampoco pueden ser el objetivo de una asignación.

El método más seguro es que la persona que llama defina la matriz y pase su dirección y tamaño a la función que se supone que debe escribir en ella:

void returnArray(const char *srcArray, size_t srcSize, char *dstArray, char dstSize)
{
  ...
  dstArray[i] = some_value_derived_from(srcArray[i]);
  ...
}

int main(void)
{
  char src[] = "This is a test";
  char dst[sizeof src];
  ...
  returnArray(src, sizeof src, dst, sizeof dst);
  ...
}

Otro método es que la función asigne la matriz dinámicamente y devuelva el puntero y el tamaño:

char *returnArray(const char *srcArray, size_t srcSize, size_t *dstSize)
{
  char *dstArray = malloc(srcSize);
  if (dstArray)
  {
    *dstSize = srcSize;
    ...
  }
  return dstArray;
}

int main(void)
{
  char src[] = "This is a test";
  char *dst;
  size_t dstSize;

  dst = returnArray(src, sizeof src, &dstSize);
  ...
  free(dst);
  ...
}

En este caso, la persona que llama es responsable de desasignar la matriz con el free función de biblioteca.

Tenga en cuenta que dst en el código anterior hay un puntero simple a charno un puntero a una matriz de char. La semántica de puntero y matriz de C es tal que puede aplicar el operador de subíndice [] a una expresión de tipo matriz o tipo de puntero; ambas cosas src[i] y dst[i] accederá a la i‘th elemento de la matriz (aunque sólo src tiene tipo de matriz).

puede declarar un puntero a una matriz de N elementos de T y hacer algo similar:

char (*returnArray(const char *srcArr, size_t srcSize))[SOME_SIZE]
{
  char (*dstArr)[SOME_SIZE] = malloc(sizeof *dstArr);
  if (dstArr)
  {
    ...
    (*dstArr)[i] = ...;
    ...
  }
  return dstArr;
}

int main(void)
{
  char src[] = "This is a test";
  char (*dst)[SOME_SIZE];
  ...
  dst = returnArray(src, sizeof src);
  ...
  printf("%c", (*dst)[j]);
  ...
}

Varios inconvenientes con lo anterior. En primer lugar, las versiones anteriores de C esperan SOME_SIZE ser una constante de tiempo de compilación, lo que significa que la función solo funcionará con un tamaño de matriz. En segundo lugar, debe eliminar la referencia del puntero antes de aplicar el subíndice, lo que desordena el código. Los punteros a arreglos funcionan mejor cuando se trata de arreglos multidimensionales.

  • Su enlace a “el desarrollo de C” se ha roto… parece que debería dirigirnos aquí: bell-labs.com/usr/dmr/www/chist.html

    – Dr.Queso

    25 de marzo de 2016 a las 2:09

  • @Kundor: ¿Qué bar recibe es un puntero, no una matriz. En el contexto de una declaración de parámetro de función, T a[N] y T a[] ambos son tratados como T *a.

    – Juan Bode

    14 de abril de 2016 a las 12:16

  • @JohnBode: ¡Tienes razón! Por alguna razón, pensé que las matrices de tamaño fijo se pasaban a la pila. Recuerdo una ocasión, hace muchos años, cuando descubrí que el tamaño de una matriz tenía que especificarse en la firma del parámetro, pero debo haberme confundido.

    – Nick Mateo

    14 de abril de 2016 a las 13:22

  • @JohnBode, en la segunda parte del código, primera línea:void returnArray(const char *srcArray, size_t srcSize, char *dstArray, char dstSize) El último parámetro debe estar en size_t tipo no char.

    – Seyfi

    17 de noviembre de 2018 a las 14:36

  • no está claro por qué esta no es la respuesta aceptada. La pregunta no era si es posible devolver un puntero a una matriz.

    usuario12411795

    18 mayo 2020 a las 16:02

  • ¿La memoria asignada para CHAR_ARRAY returned en el montón? Ciertamente no puede estar en la pila (en el marco de la pila de returnArray() ¿derecho?

    – Minh Tran

    3 de junio de 2020 a las 2:05

  • Sí, esta es la respuesta a mi pregunta: ¿Puede la función C devolver una matriz? Sí, puede, y @Indinfer lo ha respondido con el uso del tipo de datos de estructura propia de C. Y, por supuesto, debe ser de una matriz de longitud fija. Esto es C, debe ser determinista por adelantado a menos que tenga tiempo para jugar con el dolor de puntero, dirección, malloc, gratis, etc., por solo un simple retorno de función. Salud.

    – KokoEfraim

    7 de mayo de 2021 a las 7:05

  • @MinhTran Referir godbolt.org/z/1rYocv3PT – Esencialmente ring_slice se transforma en una función que acepta una dirección para almacenar. Puedes ver main reserva 32 bytes en la pila para un Mem2 (sub rsp, 32) y pasa su dirección a través de rdi para ring_slice. Realmente no conozco mis convenciones de llamadas, pero creo rdi suele ser el primer argumento de una función. ring_slice luego almacena sus resultados allí y devuelve esa misma dirección (mov rax, rdi).

    – ekipan

    1 ago 2021 a las 21:03

avatar de usuario
Mengo

Puede hacerlo usando la memoria del montón (a través de malloc() invocación) como otras respuestas informadas aquí, pero siempre debe administrar la memoria (usar gratis() función cada vez que llame a su función). También puedes hacerlo con una matriz estática:

char* returnArrayPointer() 
{
static char array[SIZE];

// do something in your array here

return array; 
}

Puede usarlo sin preocuparse por la administración de la memoria.

int main() 
{
char* myArray = returnArrayPointer();
/* use your array here */
/* don't worry to free memory here */
}

En este ejemplo, debe usar la palabra clave estática en la definición de la matriz para establecer la vida útil de la matriz durante toda la aplicación, de modo que no se destruya después de la declaración de devolución. Por supuesto, de esta manera ocupa SIZE bytes en su memoria durante toda la vida útil de la aplicación, ¡así que dimensione correctamente!

  • ¿Qué tan bueno es entregar punteros a la memoria interna de una función? Olvídese, subprocesos múltiples, esto es malo en el código de serie.

    – usuario426

    14 de noviembre de 2021 a las 15:44

En su caso, está creando una matriz en la pila y una vez que abandone el alcance de la función, la matriz se desasignará. En su lugar, cree una matriz asignada dinámicamente y devuélvale un puntero.

char * returnArray(char *arr, int size) {
    char *new_arr = malloc(sizeof(char) * size);
    for(int i = 0; i < size; ++i) {
        new_arr[i] = arr[i];
    }
    return new_arr;
}

int main() {

    char arr[7]= {1,0,0,0,0,1,1};
    char *new_arr = returnArray(arr, 7);

    // don't forget to free the memory after you're done with the array
    free(new_arr);

}

  • ¿Qué tan bueno es entregar punteros a la memoria interna de una función? Olvídese, subprocesos múltiples, esto es malo en el código de serie.

    – usuario426

    14 de noviembre de 2021 a las 15:44

avatar de usuario
Snaipe

¿Qué tal esta implementación deliciosamente malvada?

matriz.h

#define IMPORT_ARRAY(TYPE)    \
    \
struct TYPE##Array {    \
    TYPE* contents;    \
    size_t size;    \
};    \
    \
struct TYPE##Array new_##TYPE##Array() {    \
    struct TYPE##Array a;    \
    a.contents = NULL;    \
    a.size = 0;    \
    return a;    \
}    \
    \
void array_add(struct TYPE##Array* o, TYPE value) {    \
    TYPE* a = malloc((o->size + 1) * sizeof(TYPE));    \
    TYPE i;    \
    for(i = 0; i < o->size; ++i) {    \
        a[i] = o->contents[i];    \
    }    \
    ++(o->size);    \
    a[o->size - 1] = value;    \
    free(o->contents);    \
    o->contents = a;    \
}    \
void array_destroy(struct TYPE##Array* o) {    \
    free(o->contents);    \
}    \
TYPE* array_begin(struct TYPE##Array* o) {    \
    return o->contents;    \
}    \
TYPE* array_end(struct TYPE##Array* o) {    \
    return o->contents + o->size;    \
}

C Principal

#include <stdlib.h>
#include "array.h"

IMPORT_ARRAY(int);

struct intArray return_an_array() {
    struct intArray a;
    a = new_intArray();
    array_add(&a, 1);
    array_add(&a, 2);
    array_add(&a, 3);
    return a;
}

int main() {
    struct intArray a;
    int* it;
    int* begin;
    int* end;
    a = return_an_array();
    begin = array_begin(&a);
    end = array_end(&a);
    for(it = begin; it != end; ++it) {
        printf("%d ", *it);
    }
    array_destroy(&a);
    getchar();
    return 0;
}

  • Esto es lo suficientemente delicioso como para despertar mi curiosidad. ¿Puedes explicar un poco más lo que hiciste allí arriba o tal vez sugerir una lectura a esta delicia que llamas? Gracias por adelantado.

    – Unheilig

    08/01/2014 a las 19:49

  • @Unheilig: tenga en cuenta que hay algunos errores potenciales en esto, fue solo una prueba de concepto. Dicho esto, el truco es devolver un struct como un contenedor/objeto de matriz. Piense en ello como un C++ std::vector. El preprocesador expandiría el int versión de esto para struct intArray { int* contents; int size; };.

    – piroespada

    9 de enero de 2014 a las 4:05

  • Me gusta el enfoque. pro: esta es una solución genérica; contra: solución intensiva de memoria. No es óptimo para vectores de tamaños conocidos. De todos modos, esto se puede actualizar con la asignación de tamaño inicial. Definitivamente agregaría algún control de asignación. Muy buena propuesta para empezar 🙂

    – Urkon

    4 de enero de 2018 a las 13:47

  • Mix-mash atractivo de esk orientado a objetos. Me gusta.

    – Jack G.

    22 de enero de 2018 a las 3:24


  • Hermoso. Estoy impresionado

    – usuario3139868

    20 de abril de 2021 a las 17:38

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad