Duda C, funciones que devuelven Arrays

Awoke

Hola, vengo de Java y ando haciendo unos ejercicios de C y me he encontrado con una duda.

Cuando declaro un método que quiero que devuelva un array, se escribe así?:

int [] decBin(int dec);

Os paso el código que llevo.

spoiler
elkaoD

Sí, pero el problema que tienes que, si no me equivoco (llevo mucho sin tocar ANSI-C), esos arrays que intentas devolver están declarados en local en la función y por tanto se encuentran en la pila. En C los arrays nos son objetos, sino "punteros escondidos" y lo que estás devolviendo es el puntero, pero al inicializar el array en la pila no es válido.

Tienes que usar malloc para crear el array, pero entonces tienes el problema de que, como lo has inicializado dentro de la función, nunca liberas el trozo de memoria correspondiente (antes de que acabe la función tienes que hacer free() del puntero.) Podrías hacer el free en el propio main, pero es un poco chungo eso de que reserves memoria y la liberes en un sitio diferente porque tarde o temprano se te va a olvidar hacer el free y vas a tener leaks.

La opción correcta es no hacer los arrays en la función local y devolverlos, sino que la función no devuelva nada y pasarle el puntero al array a la función como "void decBin(int dec, int[] array)" y usar el parámetro "array" como salida.

1 respuesta
Buffoncete

#2 perfecto ;)

También recuerda que en Ansi-C si pasas por parámetro a una función un puntero a un array, no sabrás nunca cuántos elementos realmente tiene, ni con sizeof ni con strlen, así que de forma correcta se tiene que hacer con:

void decBin(int dec, int** array, int *numOfElements);

Y como aplicación, cuando se trabaja contra dlls de microsoft windows que devuelven arrays, primero se llama pasando null como array y la función devuelve en numOfElements cuántos elementos va a retornar en realidad, para a continuación, llamar a la funcionalidad con el array bien inicializado con malloc o calloc.

en concreto, esto ocurre utilizando la cryptoapi de windows.

--

1 respuesta
Awoke

Uff, la cosa se complica xD. Gracias de todos modos, me pongo a ello. Ahora pondré otro pedazo de código a ver si me encontrais el fallo.


NUEVO PROBLEMA.

Convertir 4 caracteres hexadecimal en decimal.

spoiler
1 respuesta
elkaoD

#4 da más datos, ¿qué falla?

Por cierto qué fealdad de código, organízate un poco mejor y no repitas en el IF tanto car1, car2, etc.

De momento no te vendría mal hacer una función para cada carácter:

int hexToDec(char hex) {
  if (hex >= '0' && hex <= '9')
    return hex-'0';
  else if (hex >= 'a' && hex <= 'f')
    return hex+10-'a';
  else if (hex >= 'A' && hex <= 'F')
    return hex+10-'A';
  else return -1;            // ERROR!
}

Devuelve el valor en decimal de 'hex' o -1 si no es un carácter hexa válido.

Luego en lugar de tener los 4 caracteres por separdo, hazte un array char caract[4]; Con este array la función a pasar a decimal es mucho más simple. Asumiendo que en el array están ordenados con el LSB en el último índice del array, la función sería tal que así:

int hexToDec(char[] hex, int numCars) {
  int total = 0;
  int i;
  for (i=0; i<count; i++) {
    int cifraEnDec = hexToDec(hex[i]);
    if (cifraEnDec == -1) return -1;     // Si la función para un carácter falla, devolver -1 (error)
    total +=  * pow(16, numCars-i-1);    // Si no hay fallo, aumentar el total por BASE^(posicion)
  }
  return total;
}

Con estas dos funciones tienes el programa hecho y BIEN. Ojo, no sé si ANSI-C permite funciones sobrecargadas así que a lo mejor tienes que cambiar el nombre.

1 respuesta
Awoke

#5 Pués, por ejemplo:

Si meto ABAB, me sale 672, y el número correcto es 43947, pienso que el error está al final, al multiplicar por la CTE.

1 respuesta
elkaoD

#6 claro que está mal, "valor = CTE * valor1 + valor2 + valor3 + valor4"

Ahí estás dando mal los pesos. En todo caso sería:

valor = CTE*CTE*CTE*valor1 + CTE*CTE*valor2 + CTE*valor3 + valor4;

En los cambios de base hay que multiplicar cada cifra por BASEposicion_desde_la_derecha

1 respuesta
Awoke

#7 Millones de gracias.

B

#3: Una pregunta (porque nunca se me ha ocurrido). Si sabes que el array es de enteros, no puedes sacar el número de elementos haciendo sizeof(array)/sizeof(int).

Nunca lo he probado pero lo he visto por ahí.

Pf, qué asco me da C de verdad. Con este tipo de cosas el código queda asqueroso.

1 respuesta
elkaoD

#9 el problema creo que es que sizeof(array) devuelve lo mismo que sizeof(<tipo>*). Vamos, que es el tamaño del puntero, no el tamaño del array completo. Los arrays no son más que una sintaxis bonita para un constructo feo.

En C un array no es un array, sino un puntero del tipo del array a su primer elemento. Si tomas el sizeof() del array de un int[] sólo estás haciendo sizeof(int*), es decir, 32 bits en plataformas x86. El acceso a los elementos subsiguientes del array es el equivalente a sumar al puntero.

Esto es así porque bajo mi punto de vista C es un lenguaje de bajo nivel comparado con casi todo el resto de lenguajes. Es como un "envoltorio bonito" alrededor de ASM y hereda muchas de sus historias. En este caso, los arrays punteros es porque en ASM funciona de forma parecida. Tú guardas la dirección del puntero en un registro (eax p.ej.) y luego para acceder a él, el equivalente a array[5] en C se hace en ASM como [eax+5], es decir lo que sería en C *(array+5). Como ves, no es más que una sintaxis bonita, pero el concepto "interno" se mantiene y de ahí que el sizeof() del array sólo devuelva el tamaño del tipo de dato real de la variable: un puntero.

EDIT: C no es que de asco, sino que es un lenguaje "pa' lo que es". Lo de que C sea tan ASM-con-llaves es bueno y malo. Por un lado como tú dices hace cosas que hacen que el código sea feo. Por otro lado, estás trabajando casi con ASM. De hecho puedes embeber instrucciones en ensamblador en mitad del código. ¿Necesitas optimización? C es tu amigo.

¿Quieres tener un bonito método length() para cada uno de tus arrays? Usa Java, pero asume que vas a estar ejecutándote sobre una máquina virtual. O usa C++ y vectores en lugar de arrays, pero asume que vas a tener una ejecución más lenta, sobrecoste por la creación de objetos, etc.

El array en C es exactamente igual que un array en memoria y es el que hace trabajar menos a la CPU, a cambio de más trabajo para el programador.

Y esa es la razón por la que me da pena que no se de C en clase. En mi universidad no se da (se da Pascal para empezar... aunque por suerte te lo hacen estudiar por tu cuenta para SSOO) y en los Grados que me han comentado, tampoco (se da C# o Java.) Sin conocer C te pierdes un eslabón importantísimo entre la CPU y los lenguajes de alto nivel.

1 respuesta
B

#10: No, si yo sé perfectamente como se tratan los arrays en C. Y yo pienso lo mismo que tu. Es decir, que si haces sizeof(array) te va a devolver el tamaño de un elemento (porque apunta al primero). Yo doy C en muchas asignaturas (ahora mismo lo estoy utilizando en 4º para programación paralela, y mola, pero es un puto coñazo, claro).

Las cosas como son. Sí, C te da un conocimiento a bajo nivel y un rendimiento muy buenos. Pero... para cualquier cosa medianamente grande es horrible utilizarlo. Como tu dices, es para lo que es.

Pero vaya, que te decía lo del sizeof(array)/sizeof(tipo) porque ya lo he visto en la hostia de webs, aunque sinceramente aún no lo he probado xD

PD: Si a mi me dicen que tengo que hacer una aplicación grande en C (como muchas de gnome, por ejemplo). Me pego un tiro xD

PD2: Me he compilado este programa:

#include<stdio.h>
#include<stdlib.h>
int main() {

int array1[10];
int * array2 = malloc(sizeof(int)*10);

printf("Tamaño array estático: %d\n", sizeof(array1)/sizeof(int));
printf("Tamaño array dinámico %d\n", sizeof(array2)/sizeof(int));

free(array2);
return 0;
}

Y en el primer caso me devuelve 10, en el segundo 1. Mi gozo en un pozo, voy a tener que darle la hostia de vueltas a mi práctica de programación paralela xDD

Usuarios habituales