Devolviendo una matriz usando C

Soy relativamente nuevo en C y necesito ayuda con los métodos relacionados con las matrices. Procedente de la progtwigción de 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 las devuelves. Siendo un progtwigdor nuevo, realmente no entiendo esto en absoluto, incluso con los muchos foros que he examinado.

Básicamente, estoy tratando de escribir un método que devuelva una matriz char en C. Proporcionaré el método (vamos a llamarlo returnArray) con una matriz. Creará una nueva matriz de la matriz anterior y le devolverá un puntero. Solo necesito ayuda sobre cómo comenzar y cómo leer el puntero una vez que se envía fuera de la matriz. Se agradece cualquier ayuda para explicar esto.

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? } 

No he probado esto todavía porque mi comstackdor de C no funciona en este momento, pero me gustaría resolverlo

No puede devolver matrices de funciones en C. También no puede (no debería) hacer esto:

 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? } 

returned se crea con una duración de almacenamiento automática y las referencias a él se invalidarán una vez que salga de su ámbito de statement, es decir, cuando la función regrese.

Tendrá que asignar dinámicamente la memoria dentro de la función o llenar un búfer preasignado provisto por la persona que llama.

Opción 1:

asignar dinámicamente la memoria dentro de la función (la persona que llama es responsable de la desasignación de ret )

 char *foo(int count) { char *ret = malloc(count); if(!ret) return NULL; for(int i = 0; i < count; ++i) ret[i] = i; return ret; } 

Llámalo así:

 int main() { char *p = foo(10); if(p) { // do stuff with p free(p); } return 0; } 

Opcion 2:

llenar un búfer preasignado proporcionado por la persona que llama (la persona que llama asigna buf y pasa a la función)

 void foo(char *buf, int count) { for(int i = 0; i < count; ++i) buf[i] = i; } 

Y llámalo así:

 int main() { char arr[10] = {0}; foo(arr, 10); // No need to deallocate because we allocated // arr with automatic storage duration. // If we had dynamically allocated it // (ie malloc or some variant) then we // would need to call free(arr) } 

El tratamiento de las matrices de C es muy diferente al de Java, y tendrás que ajustar tu 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 “matriz” en la mayoría de los contextos). En C, una expresión de tipo “matriz de elementos N de T ” se convertirá implícitamente (“decaimiento”) a una expresión de tipo “puntero a T “, excepto cuando la expresión de matriz es un operando del sizeof o unario & operadores o si la expresión de matriz es un literal de cadena que se usa para inicializar otra matriz en una statement.

Entre otras cosas, esto significa que no puede pasar una expresión de matriz a una función y hacer que se reciba 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 foo , la expresión str se convierte de tipo char [6] a char * , por lo que el primer parámetro de foo se declara char *a lugar de char a[6] . En sizeof str , dado que la expresión de matriz es un operando del operador sizeof , no se convierte a un tipo de puntero, por lo que se obtiene el número de bytes en la matriz (6).

Si está realmente interesado, puede leer El desarrollo del lenguaje C de Dennis Ritchie para comprender 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 objective 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 dinámicamente la matriz 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 la función de biblioteca free .

Tenga en cuenta que dst en el código anterior es un simple puntero a char , no un puntero a una matriz de char . La semántica del puntero y la matriz de C es tal que puede aplicar el operador de subíndice [] a una expresión de tipo de matriz o tipo de puntero; tanto src[i] como dst[i] accederán al i -ésimo elemento de la matriz (aunque src solo tenga el tipo de matriz).

Puede declarar un puntero a un conjunto 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 que SOME_SIZE sea ​​una constante en tiempo de comstackción, lo que significa que la función solo funcionará con un tamaño de matriz. En segundo lugar, debe desreferenciar el puntero antes de aplicar el subíndice, que satura el código. Los punteros a las matrices funcionan mejor cuando se trata de matrices multidimensionales.

¿Qué tal esta implementación deliciosamente malvada?

array.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  #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; } 

En su caso, está creando una matriz en la stack y una vez que abandona el scope de la función, la matriz será desasignada. En su lugar, cree una matriz dinámicamente asignada y devuelva un puntero a ella.

 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); } 

No estoy diciendo que esta sea la mejor solución o una solución preferida para el problema dado. Sin embargo, puede ser útil recordar que las funciones pueden devolver estructuras. Aunque las funciones no pueden devolver matrices, las matrices pueden ser envueltas en estructuras y la función puede devolver la estructura, llevando así la matriz con ella. Esto funciona para arreglos de longitud fija.

  #include  #include  #include  typedef struct { char v[10]; } CHAR_ARRAY; CHAR_ARRAY returnArray(CHAR_ARRAY array_in, int size) { CHAR_ARRAY returned; /* . . . methods to pull values from array, interpret them, and then create new array */ for (int i = 0; i < size; i++ ) returned.v[i] = array_in.v[i] + 1; return returned; // Works! } int main(int argc, char * argv[]) { CHAR_ARRAY array = {1,0,0,0,0,1,1}; char arrayCount = 7; CHAR_ARRAY returnedArray = returnArray(array, arrayCount); for (int i = 0; i < arrayCount; i++) printf("%d, ", returnedArray.v[i]); //is this correctly formatted? getchar(); return 0; } 

Invito a comentarios sobre las fortalezas y debilidades de esta técnica. No me he molestado en hacerlo.

Puede hacerlo utilizando la memoria de stack (a través de la invocación de malloc () ) como otras respuestas informadas aquí, pero siempre debe administrar la memoria (use la función gratuita () cada vez que llame a su función). También puede 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 palabras clave estáticas en la definición de matriz para establecer en toda la aplicación la duración de la matriz, por lo que no se destruirá después de la statement de devolución. Por supuesto, de esta forma ocupará TAMAÑO de bytes en su memoria durante toda la vida útil de la aplicación, ¡así que dimensionela correctamente!

Su método devolverá una variable de stack local que fallará gravemente. Para devolver una matriz, cree una fuera de la función, pase la dirección por la función, luego modifíquela o cree una matriz en el montón y devuelva esa variable. Ambos funcionarán, pero el primero no requiere ninguna asignación de memoria dinámica para que funcione correctamente.

 void returnArray(int size, char *retArray) { // work directly with retArray or memcpy into it from elsewhere like // memcpy(retArray, localArray, size); } #define ARRAY_SIZE 20 int main(void) { char foo[ARRAY_SIZE]; returnArray(ARRAY_SIZE, foo); } 

Puedes usar un código como este:

 char *MyFunction(some arguments...) { char *pointer = malloc(size for the new array); if (!pointer) An error occurred, abort or do something about the error. return pointer; // Return address of memory to the caller. } 

Cuando haga esto, la memoria se liberará más tarde, pasando la dirección a libre.

Hay otras opciones Una rutina puede devolver un puntero a una matriz (o parte de una matriz) que es parte de alguna estructura existente. La persona que llama puede pasar una matriz, y la rutina simplemente escribe en la matriz, en lugar de asignar espacio para una nueva matriz.