Curso completo de programación con lenguaje C

B

Ejercicios - Leccion 8

2.a
2.b
3.
4.
5.
6.
7.

Ire editando segun acabe ejercicios.

1 1 respuesta
Fyn4r

#175 te lo voy a decir como me lo dijeron a mi en su día:

"Un puntero es un miserable entero, nada más. No se por qué os liais tanto con esa tonteria"

A mi me sirvió xD

2
Skiuv

#181 En el contarHasta del 4 te has complicado la existencia xD (los otros no los vi).

Pista 1: La bandera no hace falta.
Pista 2: El return queda feo dentro del while.
Pista 3: Si "numero" no está en el vector, cuando acaba el while?
Pista 4: Esa función no debería (xD) tener más de 4 lineas (contando la definición de "i" y el return).

Las pistas no te las tomes a mal, porque tan importante es que funcione como que esté bien escrito.

2 respuestas
Lecherito

#183 Queda feo? En la mayoría de lenguajes queda mucho mejor un return que guardarlo en una variable. Hace tiempo leí una respuesta en Stackoverflow que hablaba de esto, y decía que un único return tenía sentido en los lenguajes donde tienes que liberar la memoria por ti mismo, para hacerlo todo en el mismo sitio (que quizás es nuestro caso, pero hay que entender el porqué).

En otros lenguajes que la memoria la gestionan por ti y te da igual, hay que meter un return cuando ya tienes lo que necesitas, ni más ni menos además de que para mi es mucho más legible.

1 2 respuestas
DaRk-eXe

#183 lo importante e que funcionen los ejercicios, ya habrá tiempo de optimizar las funciones, vayamos paso a paso que la gente está aprendiendo.

1 1 respuesta
gonya707

Estoy de acuerdo con #184, pero en este caso las variables se destruyen al salir de la funcion por lo que no pasa realmente nada.

Yo el problema que veo a ese ejercicio 4 es que si no se encuentra el numero a buscar, nunca se sale de la función, deberia comprobarse en el bucle 1) que no se ha encontrado aun el numero 2) que no se ha sobrepasado el limite del array

1 respuesta
Skiuv

#185 #186 Estoy de acuerdo con lo de optimizar pero yo he dicho escribir bien. Cuando yo aprendí me enseñaron a escribir. Me refiero a lo del return y a saber usar los bucles:

while((condición1) && (condición2)) i++;
return i;

No creo que saber hacer esto requiera un master. Esto hay que aprenderlo desde el principio porque las malas practicas es lo que tienen xDDD

#184 La explicación es esa pero lo de legible es cuestión de gustos.

1 respuesta
DaRk-eXe

#187 si ese bucle va como solución a lo que estamos hablando.. del ejercicio de R1kz.. está mal.

1 respuesta
Skiuv

#188 ¿eh? ¿Qué está mal? :D

1 respuesta
DaRk-eXe

#189 te voy a dar una pista..

Pista 1: No se diferencia porqué motivo ha salido del bucle y retornas lo que pilles.

1 respuesta
Skiuv

#190 El código de #187 lo puse como ejemplo de mi comentario no como solución del ejercicio. xD

willy_chaos

Pregunta sobre programacion:

Actualmente tengo nociones de lo siguiente

C, Java, PHP, PLSQL, SQL, HTML, CSS, JS, Shell pero no tengo nivel potente en ninguno y la verdad me gustaria. Hasta donde considerias que seria lo minimo que un "junior" debería saber de cada lenguaje si fuerais a contratarlo?

1 respuesta
B

#192 ¿En que campo te quieres enfocar? Web, Aplicaciones... ?

1 respuesta
willy_chaos

#193 Web hace tiempo que voy tocando pero nunca me he metido, ademas que tampoco me llama demasiado (eso de tener que pelearte con cada navegador para que se vea perfecta me aburre) y la verdad me llama mas aplicaciones de escritorio y smartphone.

Ahora en la uni estamos dando Android y Java.

1 respuesta
gonya707

#194 Por experiencia te diré que ahora mismo muchas compañias quieren gente que sepa de android, asi que además de lo que des en la carrera podría interesarte profundizar en el tema por tu parte.

De todos modos te advierto que lo que comentas con lo d eprobar en muchos navegadores tambien es algo que arrastra Android, tu app puede verse genial en tu movil pero estar fatal en todas las tablets y el 80% de los moviles, lo cual es un coñazo.

1 1 respuesta
willy_chaos

#195 Ya pero voy viendo que por ejemplo si haces bueno uso de los relativelayout, linearlayout se vera "bien" en todos, lo unico pues que tendra mas espacio en blanco entre cosas y tal, ademas siguiendo las configuraciones de pantalla predefinidas deberia verse "bien" en todas esas resoluciones, otra cosa es que un telefono chino o etc tenga una resolucion no estandar y se vea algo diferente (mas espacio en blanco etc...) pero vamos tampoco puedo hablar mucho porque estamos comenzando.

Vease yo ahora en C se hacer listas, pilas, etc... pero me veo que me falta algo. En Java estamos ahora con MVC y comenzaremos Threads en breve. Por ejemplo nos han pasado ya el proyecto para final de semestre de hacer un Memory Block

Lo vamos a hacer jugable por red y tal pero no se me veo que me falta algo .

1 respuesta
gonya707

#196 ya, en teoria, en la practica ya te adelanto que a pesar de usar relativelayouts todo se puede escojonar con una facilidad pasmosa XD

Yo creo que quizá subestimas lo que vas a aprender. Un buen método para comprobarlo podría ser que te inmiscuyeses en algún proyecto o empieces tu mismo uno. Si te faltase algo lo aprenderías entonces y quizá así te respondes a la pregunta de qué es lo que te falta

allmy

#154 1 millón de gracias, acabas de hacerme entender algo que me había ofuscado hace tiempo :)

gonya707

9 - Procesado de texto

El tratamiento de los arrays de caracteres recibe un tratamiento especial en C por la razón de no existir el tipo string, como existe en otros lenguajes como Java. Además el uso tan ligado con los punteros hace que sea casi obligatorio usar otra de las librerías estándar de C siempre que queramos procesar texto, string.h.

el salto de char a char*, string.h

Las cadenas de caracteres son especiales en C, empezando por la asignación de su valor. La primera manera para asignar valores es hacerlo en su declaración, el la que solo debemos especificar el contenido de la cadena entre comillas dobles, teniendo cuidado de no sobrepasar el límite de caracteres que hemos establecido:

char frase[40] = "Hola mundo!";
char frase1[40];

Asignando el valor de esta manera no solo pone la posición [0] el valor 'H', el valor [1] a 'o' y así en adelante, sino que todos los caracteres no asignados (en este caso 40 - 11 = 29 espacios del array) se asignan a un valor que a ojos del usuario es idéntico al espacio en blanco, es decir, no tiene aspecto gráfico. En este caso al tener una cadena de caracteres asignada y otra sin asignar, imprimir mediante un bucle los 40 caracteres que componen el array se haría de esta forma:

    for (int i = 0; i < 40; i++){
        fprintf(stdout, "%c", frase[i]);
    }

y el resultado de imprimir ambas cadenas sería el siguiente:

Todo esto ocurre al asignar un valor en la misma línea en la que declaramos el array de caracteres, pero si intentamos asignar en cualquier otro punto del programa el contenido de la misma manera el compilador nos devolverá un error, en otras palabras, la siguiente línea es errónea:

frase2 = "Esto es una cadena"; //ERROR: solo posible en declaracion

La manera de asignar un valor a una cadena en cualquier punto del programa es mediante una funcion que contiene la libraría string.h, strcpy.

char* strcpy(char *target, char *source);

strcpy "string copy" recibe de entrada dos arrays de caracteres (punteros por supuesto, ya que el nombre del array ya es un puntero) y copia el contenido del segundo en el primero. La asignación errónea de antes quedaría entonces así:

strcpy(frase2, "Esto es una cadena");

El problema con strcpy es que al contrario que asignar en declaración, no borra los espacios no usados, por lo que una cadena antes y después de usar strcpy presenta este aspecto:

Por tanto es una práctica común y recomendada inicializar todas nuestras cadenas de texto antes de operar con ellas. Para ello basta con crear un bucle que recorra todas las posiciones del vector y las asigne el valor a un espacio en blanco ' '.

A pesar de no inicializarse, las cadenas de texto pueden ser tratadas he impresas por pantalla de manera satisfactoria gracias a un mecanismo que el propio C hace transparente al usuario. Siempre que se usa una función de string.h o se asigna un valor a una cadena, el último caracter del array se asigna al valor '\0'. '\0' Es un caracter (caracter nulo) que no tiene aspecto visual y que por lo tanto no aparecerá en la consola, y que tiene el significado de "fin de cadena", por lo tanto si imprimimos una cadena con fprintf, se imprimirá siempre hasta que encuentre el '\0', que ya se ha encargado C de colocar al final. El símbolo de escape para imprimir una cadena es %s, de string.

fprintf(stdout, "%s", frase); //Imprime hasta el primer \0

Puesto que todas las cadenas van a ser terminadas con un '\0', es necesario dejar al menos un caracter de margen. Si por ejemplo se reserva una cadena para una ruta de un archivo de windows (que son hasta 256 caracteres), el array que debemos declarar es de 257 para que quepa el '\0'.

Más funciones de string.h

La librería string cuenta con 22 funciones de las que conocemos una, strcpy. Podeis consultar la lista completa junto con ejemplos aquí, pero para empezar y para hacer los ejercicios de este curso basta con saberse las más elementales y útiles, explicadas a continuación:

size_t strlen(char *str);

La función strlen (string length) mide la longitud de una cadena de caracteres, es decir mide el número de caracteres desde [0] hasta el primer '\0', y devuelve el resultado en una variable de tipo size_t. El tipo size_t está definido dentro de string.h y no es más que un número entero, por lo que puede asignarse a una variable entera sin ningún problema.

int strcmp(char *s1, char *s2); 

La función strcmp (string compare), compara dos cadenas de caracteres que se le pasan como entrada y decide si son iguales o no lo son. Cabria esperar que devolviese true cuando son iguales y false cuando no lo son, pero en realidad devuelve como resultado un número entero, que es 0 cuando las dos cadenas son iguales, >0 cuando la primera cadena es "mayor" que la segunda, y <0 en caso contrario. En la mayoría de casos es suficiente saber si las dos cadenas son iguales, por lo que sólamente hay que comparar el resultado con 0. Esta función sustituye a la sintaxis cadena1 == cadena2, que al igual que las asignaciones a cadenas, no se pueden hacer en C.

char *strcat(char*s1, char *s2);

La función strcat concatena las dos cadenas, s1 y s2 y las apila de vuelta en s1. Qué es concatenar? Simplemente apilar el contenidod e una cadena seguido de otra, es decir, si una cadena s1 tiene el valor "media", y otra cadena s2 con "vida", concatenar s1 y s2 resultaría que que el valor de s1 quedaría como "mediavida". La función se encarga sola de eliminar el '\o' de la primera cadena para que finalmente queden los dos arrays perfectamente concatenados.

int sprintf(char *cadena, char *formato, ...);

Adicionalmente quisiera comentar esta función que no pertenece a string.h, es parte de stdio.h. La función sprintf "imprime" a una string, es decir, formatea una cadena del mismo modo que se hace un fprintf, de manera que podemos meter valores de variables dentro de la cadena. Un ejemplo:

int numero = 4;
char letra = 'a';
char frase[10] = "Hola";
char resultado[100];

sprintf(resultado, "Este resultado esta formado por un numero %d, una letra %c y una cadena %s.", numero, letra, frase);
fprintf (stdout "%s", resultado)

Deberes - lección 9

1 - Crear una función a la que se le pasa una cadena de texto y detecte si la cadena es un DNI bien formado (8 numeros y una letra)

2 - Hacer una función que reciba dos cadenas s1 y s2 y decida si la cadena s1 contiene s2, es decir, siendo s1 "hola que tal" y s2 "que", el resultado deberia ser true, ya que la secuencia de caracteres "que", se encuentra en "hola que tal"

3 - Hacer una función que reciba una cadena y un caracter separador y devuelva en su salida la misma cadena separada en dos, siendo el caracter de entrada el separador. Por ejemplo para "[email protected]" con el separado '@', el resultado debería ser una cadena "nombre" y otra cadena "dominio.com".

4 - Realizar una función a la que se le pasa un texto largo y que extraiga de ahí la primera dirección de correo electrónico que encuentre. Para un ejercicio más completo, la función puede extraer todas los emial que contenga la linea, no solo el primero.

5 - Crear el ya conocido menú de opciones junto con 5 arrays de caracteres. Esta vez las opciones serán: a) Borrar el contenido de los 5 arrays, b) Listar el contenido de los arrays, c) Añadir un valor pedido por teclado al primer array que no este ya ocupado, el array no puede ser repetido, y debería dar error si el dato que estamos a punto de meter ya existe en otro de los arrays, d) borrar un solo array, del que debemos especificar el contenido, es decir, si elegimos esta opción e introducimos por teclado "casa", en el caso de que exista el array con ese contenido entre los 5 del programa, se borrará., e) salir.

2
Champi

solo puedo decir gracias

MartiONE

Soy un vago de cojones, tenía que haber empezado todo esto cuando lo vi por primera vez.

Igualmente, lo estoy haciendo a mi ritmo, muchas gracias por los aportes.

willy_chaos

Hoy me ha dado por toquetear de nuevo C ya que lo tengo algo oxidado.

Ejercicio:

Tenemos una string ya generada. Hemos de separar las vocales de las consonantes poniendo las vocales a la izquierda y las consonantes a la derecha.

int main() {
    int max = 50;
    int vocal = 0;
    int conso = 49;
    char cadena[50] = "Hola como estas? Yo estoy bastante bien jeje";
    char auxili[50] = "";
    
for (int i=0; i<max; i++) { auxili[i]=""; } for (int i=0; i<max;i++) { if ( cadena[i] == 'A' || cadena[i]=='a' || cadena[i] == 'E' || cadena[i]=='e' || cadena[i] == 'I' || cadena[i]=='i' || cadena[i] == 'O' || cadena[i]=='o' || cadena[i] == 'U' || cadena[i]=='u' ) { auxili[vocal]=cadena[i]; printf("%c > i = [%d] | v = [%d] - letra = [%c]\n",auxili[vocal],i,vocal,cadena[i]); vocal++; } else { auxili[conso]=cadena[i]; printf("%c > i = [%d] | c = [%d] - letra = [%c]\n",auxili[conso],i,conso,cadena[i]); conso--; } } printf("%s \n",auxili); for (int i=0; i<max; i++) { printf("%c",auxili[i]); } printf("\n");

Me encuentro que si hago el printf de auxili me devuelve solo las vocales, sin embargo si recorro el array de chars a mano, esta lleno

Sin embargo si hiciera printf de cadena, si que me printa bien la cadena incial, esto puede ser porque al crear el "cadena" ya le he asignado un valor, y al otro no?

1 respuesta
gonya707

#202 si, es por eso. Hay una manera mucho mejor de hacer lo que quieres. Vete escaneando la cadena inicial y ve guardando en dos arrays distintos por un lado vocales y por otro consonantes. Cuando termine de chequearse la cadena concatena los dos resultados y asi tendras vocales a un lado y consonantes a otro.

willy_chaos

Ya habia pensado en hacer varios array pero me parecia la opcion "facil" XD, luego juntarlos, pero he decidido hacerlo de esta forma.

Tambien se me ha ocurrido recorrer antes el array y contar cuantas vocales tiene, y hacer que el contador de consonantes comience en la posicion n (n = numero de vocales).

Vamos que varias formas hay, mejores, peores, pero he flipado cuando he visto que el printf sudaba de mi xD y se me ha ocurrido que podia ser eso.

Tambien recuerdo que habia una instruccion puts, pero veo que hace lo mismo.

#202 estaria bien que hicieras un apartado en #1 indicando los IDE que existen para C.

Yo para Java usaba Eclipse, para C no me terminaba de convecer DevCpp asi que siempre tire de gcc file.c -o file.exe en Linux, para PHP usaba Netbeans (para automaticamente sincronizar con el servidor, etc...), Codeblocks

gonya707

Una bonita historia para insistir en el no-uso de variables globales.

Un buen día gonya decide continuar con uno de sus alocados proyectos con hardware y se pone a programar un chip de MSP430.

Cuando ya lo tiene casi todo funcional, arregla la ultima linea que le fallaba y oh! sorpresa

region `rom' overflowed by 1962 bytes. 16,384 byte maximum)

El MSP tiene 16 kbytes de rompara poner las instrucciones dentro y me habia pasado por casi 2k. Después de una tarde perdida optimizando una librería para poner ahorrar unos 300 bytes de memoria, gonya se da cuenta de que tiene al lado de los includes unas cuantas variables globales

Haciendo calculos, gonya estima que si esas variables no estuviesen se ahorraria en torno a 100 bytes "mierda, no es suficiente", pero aprovecha para desglobalizarlas y ponerlas de manera local donde se las necesita.

gonya compila otra vez ya por hábito y casi salta de la silla al ver que se ha conseguido compilar

Moraleja: Niños, no useis variables globales

3
MaSoBa

Acabo de empezar el curso. No tengo absolutamente ni idea de esto. Es la primera vez que me meto.

Deberes 1
1 respuesta
MartiONE

He hecho los 4 primeros capitulos, dejo aqui mis deberes:

Deberes 1
Deberes 3.1
Deberes 3.2
Deberes 3.3
Deberes 3.4
Deberes 3.5
Deberes 4.1
Deberes 4.2
Deberes 4.3
Deberes 4.4
1 respuesta
gonya707

10 - Estructuras de datos

En ocasiones los tipos de variables estándar no nos son suficientes o bien conllevan un nivel de abstracción que por alguna raón no se está dispuesto a asumir. Por ejemplo en un programa grande de gestión de datos podemos tener cientos de variables char* (arrays de caracteres) para guardar DNIs, direcciones de correo, nombres + apellido... Y de cara al programa todos son char*, pero por sencillez de lectura de código y para diseñar funciones específicas para cada tipo de array, podemos crear el tipo de variable "dni"m la variable tipo "email",... etc.

creacion de nuevos tipos

La creación de nuevos tipos a partir de cosas que ya existen se hace con la palabra reservada typedef.

//typedef tipo_existente nuevo_tipo ;
typedef char dni[10];
typedef char email[100];

En este caso ambos tipos nuevos parten de un tipo ya existente, el char, y más concretamente los arrays de char, pero pueden crearse a partir de cualquiera de las otras variables conocidas. A partir de estas declaraciones de tipo, podemos usar en nuestro programa "dni" y "email" como tipos de variables.

int main(void){ 
    dni ejemplo; //variable tipo dni
    strcpy(ejemplo, "12345678A");
    
return 0; }

creacion de estructuras de datos

Pero en muchas ocasiones, incluso la creación de tipos simples como los que se crean con typedef tampoco son suficientes y es necesario manejar tipos de datos que contengan más de un dato. Por ejemplo pensemos en una variable tipo "contacto". Un contacto puede contener los siguientes datos: nombre, primer apellido, segundo apellido, dirección postal, teléfono, email y edad. Todos estos datos podrían volcarse en una cadena de caracteres muy larga, pero sería un método caótico y poco práctico. La mejor manera es crear una estructura de datos "contacto" que contenga todos estos campos.

Existen tres maneras de crear estructuras en C, podeis consultarlas en esta dirección, pero en dos de los tres casos las estructuras solo sirven para crear una sola variable de ese tipo, mientras que la que voy a explicar a continuación es la manera permanente, y la que sirve en todos los casos posibles. La manera entonces de definir una estructura es la siguiente:

typedef struct{
        char nombre[50];
        char apellido1[30];
        char apellido2[30];
        char direccion[200];
        long int telefono;
        char email[100];
        int edad;
}contacto;

La inclusión de typedef nos indica que se va a crear un tipo nuevo, y ese tipo es una estructura, struct. La estructura a su vez está formada por varios campos, de distintos tipos. Nota: el telefono lo he dejado como un numero, y puesto que es posible que no quepa en un int, la variable es un long int, que no es mas que un int con el doble de bits.

Ahora una vez comenzado el programa podemos crear cosas con este nuevo tipo estructural, variables, arrays de contactos, punteros a contactos,... Y para acceder a cada uno de sus campos se usa el punto '.'.

    contacto ejemplo;
    
strcpy(ejemplo.nombre, "John"); strcpy(ejemplo.apellido, "Doe"); ejemplo.edad = 30;

Muchos editores, entre ellos en Dev C++ tienen una opción de autocompletar, que se despliega inmediatamente al pulsar el punto, por tanto en este caso al escribir nuestra variable "ejemplo" y pulsar punto, el programa comprende que vamos a acceder a uno de los contenidos de ejemplo y nos da una opcion de autocompletar:

deberes - Lección 10

Esta vez ha sido breve, os quejareis.

  1. Crear un tipo coche, con los campos marca, modelo, caballos, CC, Velocidad punta y un numero de identificacion.

  2. Crear una funcion que "cree" y rellene una variable coche. Debe preguntar por teclado al usuario todos los campos que contiene la estructura, excepto el numero identificador, que será un número entero aleatorio de 4 cifras.

  3. Crear un programa con menú que integre una utilidad completa de gestión de un concesionario. Las opciones del maneja deben ser:

-Listar los coches existentes enseñando marca, modelo e ID.
-Añadir un coche nuevo a la lista
-Borrar un coche ya vendido. El usuario deberá meter la ID y el programa deberá borrar el coche que coincida con esa ID.
-Listar los coches de la marca introducida a continuacion por teclado
-salir

Un detalle a tener en cuenta es que el concesionario solo tiene cabida para 10 coches, asi que una vez lleno no debería poderse añadir un coche numero 11, teniendo que borrarse otro previamente.

Para este ejercicio usar una estructura coche mas simplificada que la del ejercicio 1, para evitar tener que introducir demasiados datos en cada opcion de añadir.

gonya707

#206 Me alegro que te hayas animado a empezar ^, tu programa está perfecto, sólo te recomiendo para el futuro que las variables las crees dentro del main, en vez de fuera por el momento.

#207 En el 3.1 podrías haber aprovechado que tenias math para usar la función pow en vez de poner lado1*lado1, pero da igual xD. Usas mucho else if y en muchos casos no es necesario, piensa que en muchas ocasiones las comparaciones que haces son completamente discriminatorias. Me ha gustado mucho como has estructurado el 4.4 con los operadores terciarios

1 respuesta
HeXaN

#209 ¿Vas a dar listas enlazadas simples? :3

1 respuesta