Menú

Mostrar Mensajes

Esta sección te permite ver todos los mensajes escritos por este usuario. Ten en cuenta que sólo puedes ver los mensajes escritos en zonas a las que tienes acceso en este momento.

Mostrar Mensajes Menú

Mensajes - MAFUS

#41
De string no sé, eso es de C++, pero si te puedo decir de C. Sí string.h tiene funciones que no son seguras y por ello se hicieron unas parecidas pero que el programador le decía a la función cuántos bytes tenías que usarse: por ejemplo strcpy y strncpy, strcat y strncat, etc.

También hay funcionalidad para manejar caracteres anchos.

Te recomiendo que descargues el proposal para C17 (la normal del estándar hay que pagarla) y veas qué puede hacer C https://web.archive.org/web/20181230041359if_/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf
#42
Tienes un problema en la línea 3:
Para crear los diferentes elementos del array has escrito (1,1,0,1,1),(1,1,1,1,1),(1,0,1,1,1),(1,1,1,1,1),(0,1,1,1,1)

Con los paréntesis no generas elementos, sino que agrupas operaciones. Pues bien dentro de los paréntesis estás usando el operador coma.

El operador coma es un operador binario que evalúa la primera expresión y descarta su valor, después evalúa la segunda expresión y devuelve el resultado. Encadenando expresiones, cómo lo has hecho estás devolviendo el último operando de cada grupo de paréntesis.

Por otra parte, cuándo inicializas un array se puede hacer de forma incompleta dando valor a las primeras posiciones; el compilador entiende que debe dejar las siguientes a 0 por lo que en realidad tu código genera el siguiente array:
1 1 1 1 1
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0


Para solucionar esto sólo cambia los paréntesis por llaves en tu inicialización del array.
#43
No la liberas a mano. De esa se encarga C. Por eso si intentas liberar con free te saltará el error. Y no, la memoria puedes hacer una cadena tan larga como quieras. En los sistemas modernos el propio sistema no te dejará porque impide que los programas escriban o lean en direcciones de memoria que no están autorizados. En sistemas sin protección podrías escribir una cadena tan larga que podría sobreescribir cualquier parte de la memoria, y eso eran los ataques de buffer overflow.
#44
C separa la memoria en diferentes secciones para un programa. Está la pila (stack), el montón (heap), variables locales y estáticas, sólo lectura y memoria de programa. Las variables locales y los arrays (dentro de las funciones) van a la stack y de eso se encarga C de adquirir y liberar la memoria.
De hecho una vez compilado las variables de este tipo no existen: todo son operaciones con aritmética de punteros sobre el registro Base Pointer y por eso no se puede liberar memoria con free.

La que sí debes liberar es la memoria dinámica, que se guarda en el montón (heap). Lo único que hace allí C es adquirir tantos bytes como le has pedido, además de una cierta metainformación, y guarda en una tabla interna todo esto y te devuelve la dirección de memoria del inicio de memoria. Lo único que hará es mantener desde el inicio tantos bytes como lo has solicitado para que otra petición de memoria del montón no la pise. C no quiere saber nada más: si la pierdes, si la sobreescribes, lo que sea, C va a mantener esa memoria como 'ocupada' hasta que la liberes. Y por eso la memoria del montón sí debe ser liberada: le debes decir a C que elimine esa entrada de la tabla porque él no sabe, ni le importa, qué estás haciendo con ella.
#45
Programación C/C++ / Re: Punteros en c++
30 Mayo 2021, 11:20 AM
Entiendo tu postura de dejar las cosas lo más sencillas posibles, pero tal vez esa sencillez no sea suficiente. Estarás de acuerdo conmigo que el problema del entendimiento de los punteros es recurrente en todo el que ha estudiado este lenguaje. Yo, por mi parte, hasta que no vi a los punteros de esa forma no llegué a comprenderlos del todo y por eso, creo yo, que deberían enseñarse de esa forma.

Es cierto que el programa donde se asigna una constante como dirección de memoria no le va a funcionar. Pero sirve de ejemplo para mostrar que ocurre por debajo, y de ahí la construcción progresiva hasta llegar al puntero que recoge una dirección de una variable; es decir, es para que vaya viendo el proceso natural de lo que ocurre en la máquina.

O tal vez es que por mi curiosidad me gusta saber esas cosas.

Tienes razón, tal vez no se dedique a hacer firmwares, pero quien sabe si un estudiante de electrónica si ve este post y está interesado en saber cómo funcionan los punteros en C++. Siempre intento dar las respuestas pensando en un amplio espectro de gente que pueda leerla.

Y si, tienes razón, el esquema de memoria variará de arquitectura a arquitectura y por ende el de las llamadas a funciones. Pero también es un punto a tener en cuenta.
#46
Programación C/C++ / Re: Punteros en c++
29 Mayo 2021, 18:00 PM
Sí, pero para entender una cosa realmente es mejor empezar desde los cimientos y los cimientos, en punteros, son que es un número que representa una dirección de memoria. Y de ahí para arriba. Estarás de acuerdo conmigo que realmente no se puede entender C sin entender el esquema de memoria. Al menos eso me sirvió a mi: entonces todo fue más fácil. Eso y la traducción a ensamblador de la convención de llamadas a funciones, para entender las variables automáticas.




Supongo que no será el caso de mucha gente que visite el foro pero este lenguaje es muy habitual verlo en microcontroladores y aquí se mapean en memoria muchos registros de configuración y los puertos E/S. En PC usar constantes para acceder a memoria no es útil, pero en el mundo del firmware es la norma.
#47
Programación C/C++ / Re: Punteros en c++
29 Mayo 2021, 12:55 PM
Puntero a lo easy.

Un puntero es un número (cómo todo dentro de un ordenador) y ese número representa una dirección de memoria.

Por ejemplo:
#include <stdio.h>

int main() {
   printf("%d", *(int*)0x61FE1C);
}


En el ejemplo anterior directamente le digo a C que me muestre qué hay en la dirección de memoria 61FE1C. ¿Se puede? Sí. Por cierto, ese casting (int*) es necesario para convertir el literal en un puntero. El * que hay delante nos muestra el contenido.

* Un poco más avanzado: En este caso he hecho un programa previo para ver dónde residía la memoria de datos del programa y pudiera apuntar ahí con este ejemplo. Los sistemas operativos actuales y los microprocesadores funcionan por capas. No van a dejar que un programa de usuario pueda leer o escribir fuera de su área designada. Antes sí que se podía, o en sistemas sin protección sí puede hacerse.

Podemos usar una variable para alojar esa dirección de memoria del ejemplo anterior:
#include <stdio.h>

int main() {
   int *p = (int*)0x61FE1C;
   printf("%d", *p);
}


Aquí asignamos el literal a una variable puntero. Las variables puntero se denotan por anteponer el asterisco (*) delante del nombre durante su definición o declaración. Para ver la definición y declaración por separado sería así:

#include <stdio.h>

int main() {
   int *p;
   p = (int*)0x61FE1C;
   printf("%d", *p);
}


Nótese que cuándo se hace la definición (se le da valor) no se usa el asterisco (*), pues éste tiene la función, en sus posteriores usos, de conseguir el valor guardado en esa posición de memoria, que en este caso es la posición 0x61FE1C.

Pero cómo hemos dicho el conjunto sistema operativo/microprocesador no te deja apuntar a zonas arbitrarias de la memoria, el sistema te la tiene que dar y ahí es cuando entra C que es quien negocia con el sistema para conseguir memoria libre; nosotros no debemos preocuparnos por nada de eso (afortunadamente). Por tanto cuando haces

int n;

C se encarga de reclamar una zona de memoria donde cabe un entero y el sistema se la otorga. ¿Cuál? La verdad es que no nos importa. Ídem para

int *n = malloc(sizeof(int));

Hace lo mismo: pide al sistema una zona de memoria dónde quepa un entero y éste, si tiene, nos la da.

* Un poco más avanzado: la memoria de un programa en C está dividida en sectores que son la memoria de programa, la memoria de la pila y la memoria del montón (heap). Hay más. La pila es donde caen las variables normales como int, char, double, arrays, structs, etcétera y es C el encargado de manejarlas. En el montón van a parar los malloc, calloc y toda esa família; C no entra ahí y es el propio programador el encargado de comprobar que se ha tomado memoria, de liberarla para futuros usos demás menesteres de administración.

Lo normal de un puntero es recibir la dirección de memoria de otra variable:
int n;
int *p = &n


El ampersand (&) es un operador que (en este caso) dice 'La dirección de'.

También es muy importante de los punteros el tipo de dato al que apuntan. Has visto en todos estos ejemplos cómo siempre han sido a entero (int *), pero pueden ser de cualquier tipo de dato: char *, double *, struct mi_estructura *, etcétera. Esto es debido  a que los diferentes tipos de datos ocupan tamaños en bytes diferentes en memoria y el compilador necesita saber cuánto ocupa para recuperarlo o para guardar algo en él. Por ejemplo un char suele ser de 1 byte y un entero de 4 bytes. Si los punteros no especificaran el tamaño con su tipo podríamos guardar un número muy grande en una posición dónde se guarda un char machacando datos vecinos: intentaríamos guardar 4 bytes dónde sólo cabe 1.

Y bueno, esto es lo básico de los punteros. Hay más cosas: punteros como argumentos en funciones y porqué usarlos; similitudes y diferencias entre punteros y arrays; punteros a funciones (sí, también se pueden apuntar a funciones); punteros a punteros (por loco que parezca muchas veces son necesarios). Pero eso ya para más adelante. Ahora intenta comprender sus bases.
#48
Programación C/C++ / Re: Caracteres chinos ?
13 Mayo 2021, 07:54 AM
Tendrás que instalar ese juego de caracteres en el ordenador, después cambiar el locale en el código y en la consola. O buscar una librería que te permita usar UTF, pero recuerda tambien cambiar la consola a UTF.
#49
La siguiente función debe usarse sobre un array ya ordenado.
array contiene el array ordenado
tam contiene el número de elementos de dicho array

Cuando la función ha terminado

array contiene los elementos sin duplicar en su parte más baja
tam marca el índice más alto a partir del cual los datos son inválidos. Indica el nuevo tamaño del array.

El código es en C:

void array_eliminar_duplicados(int array[], size_t *tam) {
   size_t k = *tam;
   for(int i=0, j=1; j<k; ++i, ++j) {
       if(array[i] >= array[j]) {
           --*tam;
           while(array[i] >= array[j] && j<k) {
               ++j;
           }
       }
       array[i+1] = array[j];
   }
}
#50
Sigues teniendo muchos fallos lógicos, por ejemplo:

Línea 98, falta '&&especiales' en la condición de continuación
for (int i=0; i<strlen(CARACTERES_ESPECIALES) && especiales; i++)

Línea 109, usas *argv[1] cómo valor para dimensionar el un array dinámico. Está mal porque ves el entero que representa el primer carácter de la cadena argv[1], no el entero que debería ser. Habíamos dicho que quien guardaba el tamaño de la contraseña era la variable longitud_clave
char*clave = (char*)malloc((longitud_clave+1)*sizeof(char));

Algo parecido lo anterior en la línea 110, 120 y 130.

Línea 114, usas argc en vez de i para poner el carácter nulo. Tendrás que trabajar en ello porque i, tal y como está, no puede verse fuera del bucle. Tienes una solución a un bucle anterior en el que usas 'index'.

Ya con esto tienes unas pautas para seguir arreglando el código.
[code]clave[i] = 0;
[/code]