Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: Littlehorse en 12 Diciembre 2009, 18:15 pm



Título: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Littlehorse en 12 Diciembre 2009, 18:15 pm
Este articulo también se encuentra en wiki.elhacker.net (http://wiki.elhacker.net/programacion/cc/articulos/lo-que-no-hay-que-hacer-en-c-c),
Código:
http://wiki.elhacker.net/programacion/cc/articulos/lo-que-no-hay-que-hacer-en-c-c

La mayoría que recién esta empezando con C/C++ y viene al foro a sacarse dudas, cometen errores similares. Incluso muchos tienen conceptos erroneos sobre la utilidad o eficiencia de ciertas funciones, probablemente debido a el estudio de textos incorrectos o de libros anticuados.

Pero incluso a veces uno no puede escapar de ciertas cosas. Imaginen a alguien que recién entra en la universidad y el profesor le hace incluir conio para utilizar getch. Seguramente el alumno de por sentado que hacer eso esta perfecto, e incluso el profesor recompense con notas altas a los que programan tal cual se ha enseñado.
Por lo tanto, ciertas cosas son comprensibles y el único consejo que se puede dar es que nunca den por sentado algo, es recomendable informarse antes para ver que es lo que se esta realizando, sobre todo en programación.

Para los que quieran revisar un poco explicaciones mas detalladas del "por que" de ciertas reglas, puede revisar este draft, que aunque puede tornarse bastante técnico para alguien que recién comienza, puede servir bastante:

WG14/N1256 Committee Draft — Septermber 7, 2007 ISO/IEC 9899:TC3 (http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf) (Gracias Queta)


1)
gets();

La falta de control en un programa es un factor bastante negativo, y también peligroso en caso de programas comerciales o masivos. Ese es el principal problema de gets, no tiene control interno.

¿Que signifca no tener control interno?

Significa que la funcion acarrea como consecuencia el mal comportamiento del programa, resultados inesperados y erroneos, vulnerabilidades entre otras tantas cosas.
Define una cadena de 10 caracteres, y gets va a aceptarte eso y muchisimo mas como entrada.

Veamos este codigo:

Código
  1. #include<stdio.h>
  2.  
  3. int main()
  4. {
  5.  
  6. char letra1[]="AAAAA";
  7. char letra2[]="BBBBB";
  8. char letra3[]="CCCCC";
  9.  
  10. printf("A: %s\nB: %s\nC: %s\n\n",letra1,letra2,letra3);
  11.  
  12. printf("Ingrese la letra D, 5 veces: ");
  13. gets(letra2);
  14.  
  15. printf("\nA: %s\nB: %s\nC: %s\n\n",letra1,letra2,letra3);
  16.  
  17. }

Código:
A: AAAAA
B: BBBBB
C: CCCCC

Ingrese la letra D, 5 veces: DDDDD

A: AAAAA
B: DDDDD
C: CCCCC

Como vemos nada paso, y los elementos de letra2 se reemplazaron por el input ingresado como debería ser . Ahora en vez de 5 veces, ingresemos la letra 'D' mas veces de lo que el programa nos indica:

Código:
A: AAAAA
B: BBBBB
C: CCCCC

Ingrese la letra D, 5 veces: DDDDDDDDDDDDDDDDDDD

A: DDD
B: DDDDD
C: CCCCC

Que paso con letra1? :huh: precisamente lo que no queremos que pase en nuestro programa. gets sobreescribio una zona de memoria que no debia sobreescribir, ya que el input debia ser ingresado en letra2, pero simplemente no le importo que el input sea muchisimo mas grande que el tamaño de la cadena. Razon mas que suficiente para no utilizarla.

Por lo tanto es recomendable utilizar fgets();

Código
  1. fgets(char *string, int length, FILE * stream)

Es decir:

Código
  1. fgets(letra2, 5, stdin);

Con fgets permites que sean ingresados lenght-1 caracteres.
Aunque no todo es color de rosas, y en caso de que se ingresen menos caracteres de los que has definido como tamaño, tendras que lidiar con un salto de linea '\n'. Obviamente es una nimiedad que puedes arreglarla de esta forma:


Código
  1. if (letra2[strlen(letra2)-1] == '\n')//string.h para strlen();
  2. letra2[strlen(letra2)-1] = '\0';

Supongo que con esto queda claro que no tiene sentido utilizar gets();.

Links:
http://en.wikipedia.org/wiki/Fgets
http://www.gidnetwork.com/b-56.html


2) fflush(stdin);

fflush(stdin) es un invitado casi diario. Pocas veces pasa un dia sin que alguien lo recomiende o lo mencione como la solucion! a los malos comportamientos de las pausas en los programas.

STDIN, como su nombre lo indica, significa 'Standard input'. Es decir, el ingreso por teclado.

Acorde al Standard, fflush espera solamente un stream de salida (STDOUT: 'Standard Output) por lo que el comportamiento con streams de entrada como STDIN es indefinido. Por mas que en algunas plataformas funcione, o que en algunos compiladores funcione, no deberia ser utilizado.

Por el otro lado, para evitar esas pausas fastidiosas es necesario evitar las funciones que dejan basura por doquier (como scanf();) y utilizar funciones como la ya mencionada fgets();


http://foro.elhacker.net/programacion_cc/fflushstdin-t91101.0.html
http://foro.elhacker.net/programacion_cc/zanjar_de_una_vez_fflushstdin-t265125.0.html;msg1294511
http://foro.elhacker.net/programacion_cc/error_super_basico_en_c_quien_me_ayuda_soy_nuevo-t262118.0.html;msg1275898
http://foro.elhacker.net/programacion_cc/problema_con_el_compilador_gcc_de_ubuntu-t248582.0.html




3) system("PAUSE");

Otro invitado diario es la pausa utilizando system. Esto incluso me ha tocado en mi universidad. Que el profesor me haga realizar ejercicios exclusivamente utilizando system y la pausa tambien habia que realizarla exclusivamente utilizando system. En fin, seguramente muchos consideren que realizar esto no es un gran problema, y probablemente tengan razon en los codigos que uno realiza como tarea estudiantil. Pero es un habito malo, y como todo, hay que dejarlo algun dia. Claramente no puedes pasarte toda la vida llamando al sistema para hacer una pausa.

*No es portable, solo va a funcionar en sistemas que tengan el comando PAUSE.


*Es una funcion pesada. Llamar al sistema, suspender tu programa, buscar el comando PAUSE, etc etc etc etc.

Por el otro lado si se siguio la norma de no dejar basura en el buffer de entrada, se puede utilizar soluciones mas sencillas como getchar(); (o cin.get(); en C++).


4)
conio.h

Librerias inutiles si las hay, pero por sobre todas las cosas NO standard. No existe problema para el cual sea exclusivamente necesario utilizar conio.h. Y visto que aca la mayoria de los que la incluyen lo hacen para utilizar getch(); supongo que con todo el parrafo anterior habran quedado claras las alternativas.

http://en.wikipedia.org/wiki/Conio.h
http://www.gidforums.com/t-7379.html


5) void main();

Seguramente para justificar esto habría que decir que el estándar dice que void no puede ser el valor de retorno del main, lo cual es verdad, pero no siempre es suficiente. Sobretodo considerando que muchas veces en los libros el void main dice presente.

No voy a extenderme mucho con esto ya que voy a dejar los links necesarios para el que quiera entender porque sucede, pero como base comprendamos esto:
 
-Devolver un valor void es perder la garantia que un valor de retorno distinto de 0 implica un error, lo cual es malo tanto para el SO como para el determinado programa que pueda estar llamando a tu código.

-Esta mal acorde al estándar.

-Puede que tu programa no funcione correctamente.

Los errores pueden depender de los mecanismos de retorno o la arquitectura especifica en la cual estemos. Pero por supuesto, si elegimos un lenguaje de alto nivel como lo es C o C++, es para no tener que preocuparnos por esos asuntos.

En conclusión, el que quiera entender el " por que", leer los siguientes links.

Links:

http://www.eskimo.com/~scs/readings/voidmain.960823.html
http://home.att.net/~jackklein/ctips01.html#int_main
http://users.aber.ac.uk/auj/voidmain.shtml


6) Procesamiento de cadenas: string.h

La mayoría de las funciones que no tienen la posibilidad de pasar como argumento el tamaño del buffer que debe procesarse, estan completamente expuestas al desbordamiento de buffer. Sobre todo si el contenido del buffer origen es ingresado por el usuario, y por sobre todas las cosas, si no se hicieron las validaciones correspondientes. (Por ejemplo, haber utilizado gets o no haber chequeado el tamaño de la cadena antes de copiar/concatenar o cualquier otra forma de procesar una cadena).
Obviamente no voy a hablar sobre todas las funciones, pero si sobre dos ejemplos que dan la pauta de lo que sucede y de lo que debería hacerse: strcpy y strcat.

a) strcpy ( char * destination, const char * source );

Veamos un ejemplo de un codigo incorrecto:


Código
  1. #include <stdio.h>
  2. #include <string.h>
  3.  
  4. int main()
  5. {
  6. char cadena1[]={"aaaa"};
  7. char cadena2[100];
  8. char cadena3[]={"0123"};
  9.  
  10. printf("Ingrese cadena: ");
  11. fgets(cadena2,100,stdin);
  12. printf("cadena1: %s\ncadena2: %s\ncadena3: %s\n\n",cadena1,cadena2,cadena3);
  13. strcpy(cadena1,cadena2);
  14. printf("cadena1: %s\ncadena2: %s\ncadena3: %s",cadena1,cadena2,cadena3);
  15. return 0;
  16. }

Código:
Ingrese cadena: 1111111111111111111111111111111111111111111111111111

//Antes de strcpy
cadena1: aaaa
cadena2: 111111111111111111111111111111111111
cadena3: 0123

//Despues de strcpy
cadena1: 111111111111111111111111111111111111
cadena2: 111111111111111111111111111111111111
cadena3: 1111111111111111111111111111111111111111111111111111

Como se ve claramente, el problema esta vez no estuvo en la recepción de los datos (en el sentido técnico), si no en el copiado. Ya que strcpy no verifica si el buffer destino es efectivamente capaz de contener los datos del buffer origen, lo cual por supuesto ocasiona perdida de datos y buffers overflows.
Obviamente si el fgets se hubiese utilizado correctamente, esto no hubiese sucedido.

b) strcat ( char * destination, const char * source );

La misma historia sucede aqui. Al no poder especificar el tamaño del buffer destino la funcion es insegura. Veamos un ejemplo incorrecto:

Código
  1. #include <stdio.h>
  2. #include <string.h>
  3.  
  4. int main()
  5. {
  6. char cadena1[]={"aaaa"};
  7. char cadena2[100];
  8. char cadena3[]={"0123"};
  9.  
  10. printf("Ingrese cadena: ");
  11. fgets(cadena2,100,stdin);
  12. printf("cadena1: %s\ncadena2: %s\ncadena3: %s\n\n",cadena1,cadena2,cadena3);
  13. strcat(cadena1,cadena2);
  14. printf("cadena1: %s\ncadena2: %s\ncadena3: %s",cadena1,cadena2,cadena3);
  15. return 0;
  16. }

Código:
Ingrese cadena: 11111111111111111111111111111111111111111111

//Antes de strcat
cadena1: aaaa
cadena2: 1111111111111111111111111111
cadena3: 0123


//Despues de strcat
cadena1: aaaa1111111111111111111111111111
cadena2: 1111111111111111111111111111
cadena3: 11111111111111111111111111111111111111111111

Como pueden ver suceden errores muy similares. Obviamente es un error tonto de programación en el cual el desarrollador pasa un mal argumento a fgets, ya que de haberlo hecho bien el error no sucederia. Pero ese no es el asunto, lo importante es que tanto strcpy o strcat (y toda la familia de funciones) no realizan (o permiten realizar) la validación extra necesaria. Imaginen que creas una cadena para tomar una ruta del sistema operativo para luego concatenar, ¿Y si el atacante se da cuenta de eso y decide explotar el overflow por ese lado? mil variantes pueden existir y probablemente no siempre uno cubra todas ellas.

c) Entonces que se puede utilizar?

strncpy y strncat cumplen su función perfectamente si se utilizan en forma correcta.

c1) strncpy ( char * destination, const char * source, size_t num );

Código
  1. #include <stdio.h>
  2. #include <string.h>
  3.  
  4. int main()
  5. {
  6. char cadena1[]={"aaaa"};
  7. char cadena2[100];
  8. char cadena3[]={"0123"};
  9.  
  10. printf("Ingrese cadena: ");
  11. fgets(cadena2,100,stdin);
  12. printf("cadena1: %s\ncadena2: %s\ncadena3: %s\n\n",cadena1,cadena2,cadena3);
  13. strncpy(cadena1,cadena2,1);
  14. printf("cadena1: %s\ncadena2: %s\ncadena3: %s",cadena1,cadena2,cadena3);
  15. return 0;
  16. }

Código:
Ingrese cadena: 1111111111111111111

//Antes de strncpy
cadena1: aaaa
cadena2: 1111111111111111111
cadena3: 0123

//Despues de strncpy
cadena1: 1aaa
cadena2: 1111111111111111111
cadena3: 0123

c2) strncat ( char * destination, const char * source, size_t num );

Código
  1. #include <stdio.h>
  2. #include <string.h>
  3.  
  4. int main()
  5. {
  6. char cadena1[]={"aaaa"};
  7. char cadena2[100];
  8. char cadena3[]={"0123"};
  9.  
  10. printf("Ingrese cadena: ");
  11. fgets(cadena2,100,stdin);
  12. printf("cadena1: %s\ncadena2: %s\ncadena3: %s\n\n",cadena1,cadena2,cadena3);
  13. strncat(cadena1,cadena2,0);//Obviamente esta linea es un absurdo
  14. //Pero queda claro el control que la funcion mantiene
  15. printf("cadena1: %s\ncadena2: %s\ncadena3: %s",cadena1,cadena2,cadena3);
  16. return 0;
  17. }

Código:
Ingrese cadena: 123123

//Antes de strncat
cadena1: aaaa
cadena2: 123123
cadena3: 0123

//Despues de strncat
cadena1: aaaa
cadena2: 123123
cadena3: 0123

Estas ultimas funciones a pesar de poder utilizarse perfectamente como solucion a lo anterior expuesto, tambien tienen sus contras:

-Tanto strncpy o strncat no proveen un valor de retorno que pueda implicar un error o el exito de la cadena resultante, si no que devuelven un puntero al buffer destino. Por lo tanto requiere un esfuerzo extra por parte del programador.

- strncpy no finaliza la cadena resultante con null si el destino es al menos igual en tamaño que el origen, por lo tanto siempre debe finalizarse la cadena con null después de la llamada a strncpy.

- strncpy tambien tiene un comportamiento que puede afectar el rendimiento del programa en caso que el buffer destino sea considerablemente mas grande que el buffer origen, ya que en este caso se realiza el zero-padding, es decir, llena el resto de la cadena con nulls.

Microsoft tiene una lista de funciones baneadas (lo que en Visual Studio se conoce como deprecated) y obviamente tiene el reemplazo de estas llamandolas funcion_s. Ejemplo: strcpy_s, strcat_s, gets_s y etc.
Pueden ver las funciones catalogadas como inseguras en este header (http://download.microsoft.com/download/2/e/b/2ebac853-63b7-49b4-b66f-9fd85f37c0f5/banned.h), y su reemplazo en esta lista (http://msdn.microsoft.com/en-us/library/bb288454.aspx). (Gracias Vertex por los links)

Tambien existen opciones como strlcpy y strlcat, las cuales pueden ver a fondo en este link (http://en.wikipedia.org/wiki/Strlcpy).


------------------------------------------------------------------------------------------

Continua







Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: invisible_hack en 12 Diciembre 2009, 18:55 pm
Buen post, por lo menos a mi me será útil jeje

Y pues sí, yo también pienso que hay que evitar usar system ya que es como usar comandos de Ms-Dos en C/C++ y pues si estamos con C, programamos en C, y si estamos con Batch, usamos comandos del cmd, en ese caso sí...

Edito: acabo de ver que le han puesto chincheta, se la merece si señor jeje  ;)

Saludos.


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: isseu en 13 Diciembre 2009, 13:41 pm
muy util, muchas gracias


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: xtermsh en 13 Diciembre 2009, 14:47 pm
Muy bueno littlehorse, por fin un aporte que sirve, quizas con el tiempo lo puedas ampliar, sería genial.



Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: WaRc3L en 14 Diciembre 2009, 21:06 pm
Genial Idea Littlehorse !  ;D

Espero que se vaya ampliando...  :)

Saludos!

WaRc3L


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Jaixon Jax en 16 Diciembre 2009, 03:15 am
 :rolleyes:

  Buena explicacion yo agregaria las funciones de manejo de cadenas que tienen vulnerabilidades graves espero la proxima entrega  ;D

  Saludos ....


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Foxy Rider en 16 Diciembre 2009, 06:00 am
hay mucho que se le podría agregar, pero, es tarde, tengo sueño y no se me ocurre que ahora  :rolleyes:

lo que me llega a la mente por lo que mencionó jaixxon es de  agregar las funciones "baneadas" por microsoft --> http://msdn.microsoft.com/en-us/library/bb288454.aspx (banned.h ->  http://download.microsoft.com/download/2/e/b/2ebac853-63b7-49b4-b66f-9fd85f37c0f5/banned.h )



Saludos ~


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: O-LLOS-O en 18 Diciembre 2009, 08:38 am
Esta muy bien gracias!!!

hay mucho que se le podría agregar, pero, es tarde, tengo sueño y no se me ocurre que ahora  :rolleyes:

lo que me llega a la mente por lo que mencionó jaixxon es de  agregar las funciones "baneadas" por microsoft --> http://msdn.microsoft.com/en-us/library/bb288454.aspx (banned.h ->  http://download.microsoft.com/download/2/e/b/2ebac853-63b7-49b4-b66f-9fd85f37c0f5/banned.h )



Saludos ~


microsoft ha baneado funciones?xdxdxdxdxd


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: .;. en 30 Diciembre 2009, 12:05 pm
Bien, bien


Buen post, al fin alguien que se dispone a hacer algo para el movimiento anti-conio

Y demás "fallos" típicos.

Gracias littlehorse ;)


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: goditozor en 22 Enero 2010, 02:28 am
Hola littlehorse excelente post, disculpa mi ignorancia tengo una preguntita que esto de los flujos.

cito

"fgets(char *string, int length, FILE * stream)"

cuando se usa stdin o algun otro no me ha quedado claro ese parametro..?





Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Littlehorse en 22 Enero 2010, 02:49 am
El tercer parametro de fgets es el stream desde donde vas a leer los datos. Podria ser por ejemplo un archivo de texto.

Algo asi:

Código
  1. FILE *ptr;
  2. char buff[50];
  3.  
  4. ptr=fopen("archivo.txt","rt");
  5.  
  6. if(ptr!=NULL)
  7. fgets(buff,10,ptr);/*Aqui leemos desde un archivo de texto*/
  8. puts(buff);

Ahora, al usar stdin, los datos provienen del teclado (Standard Input).

Código
  1. char buff[50];
  2. fgets(buff,10,stdin); /*Aqui se lee lo que el usuario escribe en su teclado*/
  3. puts(buff);


http://en.wikipedia.org/wiki/Standard_streams

Un saludo
 


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Aberroncho en 5 Marzo 2010, 22:53 pm
Estoy de acuerdo en todo lo dicho excepto en la parte de "string.h"


Microsoft tiene una lista de funciones baneadas (lo que en Visual Studio se conoce como deprecated) y obviamente tiene el reemplazo de estas llamandolas funcion_s. Ejemplo: strcpy_s, strcat_s, gets_s y etc.
Pueden ver las funciones catalogadas como inseguras en este header (http://download.microsoft.com/download/2/e/b/2ebac853-63b7-49b4-b66f-9fd85f37c0f5/banned.h), y su reemplazo en esta lista (http://msdn.microsoft.com/en-us/library/bb288454.aspx). (Gracias Vertex por los links  :))

Tambien existen opciones como strlcpy y strlcat, las cuales pueden ver a fondo en este link (http://en.wikipedia.org/wiki/Strlcpy).


Las funciones strcpy(), strcat(), strncpy() y strncat() pertenecen al estándar ANSI, lo que significa que están en las librerías estándar de todos los compiladores de C y funcionan igual en todas partes.

Las funciones que propones para reemplazarlas no son estándar. Las que ha implementado Microsoft es fácil que solo las encuentres en sus compiladores, y por lo que puedo leer en el enlace que has puesto, las strlcpy y strlcat no están en las librerías GNU C. Además, suponiendo que las tengas implementadas en diferentes librerías de compiladores, al no estar estandarizadas, no tienen porqué estar implementadas de la misma forma en las diferentes librerías de los diferentes compiladores por lo que su funcionalidad podría cambiarte ligeramente de un compilador a otro.

Los mayores problemas de strcpy(), strcat(), strncpy() y strncat() se dan por un mal uso de dichas funciones, osea, por errores de programación. Considero que es mejor aprender a utilizarlas adecuadamente y tener cuidado en su manejo que sustituirlas por funciones no estándar.


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Littlehorse en 5 Marzo 2010, 23:47 pm
Si obviamente, tenes razón. Por eso en el párrafo anterior expuse que strnxxx se pueden utilizar perfectamente mientras se tomen en cuenta las contras que estas tienen. El resto son solo mas opciones.

Un saludo!


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: RockAqp en 6 Abril 2010, 17:12 pm
una pregunta
no se muxo de c++
pero si no pones el system pause
en un bucle x ejemplo menu
se borra y no sale la respuesta
y tu tambien pusiste que no se deve usar el getch
tons que uso para que no se borre el resultado...
:S


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Littlehorse en 6 Abril 2010, 17:20 pm
cin.get(); en C++, puedes hacer un ignore antes para ignorar lo que te haya quedado en el buffer. getchar en C, o puedes hacer un fgets para evitar el buffer sobrante. Podrás encontrar muchas formas para hacer una pausa, si se recomienda no utilizar getch es porque proviene de una librería no estándar.
En el foro tienes muchos ejemplos y muchos hilos respecto al tema.

Saludos!


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: LeGNa29A en 7 Abril 2010, 12:00 pm
Considero que con textos como este y (junto con las opiniones que se han dado) es realmente como podemos mejorar.
Muy buen aporte, felicidades!


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: doripunk15 en 26 Abril 2010, 04:10 am
Hola!!!

Oye me dejaste con la intriga del scanf() =s.

Tu que dominas mas de C que funcion es buena para poder obtener numeros??? ya que el scanf() deja basura

Saludos y me gusto mucho tu tutorial


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: -Ramc- en 26 Abril 2010, 16:18 pm
Hola!!!

Oye me dejaste con la intriga del scanf() =s.

Tu que dominas mas de C que funcion es buena para poder obtener numeros??? ya que el scanf() deja basura

Saludos y me gusto mucho tu tutorial
Puedes usar lo que quieras para leer, excepto gets, la cosa es que limpies el buffer después, sería mejor que en vez de scanf uses, fgets + sscanf.


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Eternal Idol en 7 Mayo 2010, 11:34 am
Puedes usar lo que quieras para leer, excepto gets, la cosa es que limpies el buffer después, sería mejor que en vez de scanf uses, fgets + sscanf.

Exacto, fgets y sscanf es lo ideal, no tenes que preocuparte por ningun buffer.


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: doripunk15 en 7 Mayo 2010, 18:39 pm
Ok entonces usare estas funciones y las probare con el metodo antes descrito en el "tutorial"

Gracias por la respuesta y saludos!!!!


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Capitan ShinChan en 25 Mayo 2010, 23:16 pm
Una puntualización, corregidme si me equivoco.

Yo suelo programar de vez en cuando en C, sobretodo programas simples para hacer tareas rutinarias de cálculo y trabajo básico con archivo. Además de algunas cosas más complejas orientadas a sistemas operativos (los clásicos problemas de los filósofos, productor-consumidor, etc...).

Suelo usar a menudo la funcion strcpy y nunca he tenido ningún problema. Creo que antes que ir a buscar funciones alternativas, deberíamos pararnos y ver cómo podría fallar nuestro código. Con una simple sentencia if-else se pueden ahorrar muchos problemas. Es más, la mayoría de mis códigos están llenos de condicionales de este tipo para evitar por ejemplo que se produzcan sobreescrituras de memoria, comprobar que las funciones no devuelven ningún error, etc...


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: @synthesize en 4 Junio 2010, 01:14 am
Una puntualización, corregidme si me equivoco.

Yo suelo programar de vez en cuando en C, sobretodo programas simples para hacer tareas rutinarias de cálculo y trabajo básico con archivo. Además de algunas cosas más complejas orientadas a sistemas operativos (los clásicos problemas de los filósofos, productor-consumidor, etc...).

Suelo usar a menudo la funcion strcpy y nunca he tenido ningún problema. Creo que antes que ir a buscar funciones alternativas, deberíamos pararnos y ver cómo podría fallar nuestro código. Con una simple sentencia if-else se pueden ahorrar muchos problemas. Es más, la mayoría de mis códigos están llenos de condicionales de este tipo para evitar por ejemplo que se produzcan sobreescrituras de memoria, comprobar que las funciones no devuelven ningún error, etc...


Con un strncpy te ahorras estar comprobado código con if-else...


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: abelique en 27 Junio 2010, 02:27 am
gracias copañero un buen aporte
solo soy un novato en c\c++
y me ha encantado la informacion aportada y la manera tan detallada de explicarlo todo
De verdad gracias


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: rob1104 en 6 Agosto 2010, 08:49 am
Todo muy bien, pero solo tengo una duda.

Por ejemplo, si la función gets() es peligrosa e inestable, ¿Por qué sigue en el estandar?, ¿Solo por compatibilidad?

Saludos


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Blackhawk222 en 8 Agosto 2010, 06:47 am
Muy bueno Estoy por empezar a programar en C++ y seguro me va a servir.
Gracias Y Saludos


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Eternal Idol en 8 Agosto 2010, 12:33 pm
Muy bueno Estoy por empezar a programar en C++ y seguro me va a servir.
Gracias Y Saludos

Entiendo que el avatar sea un chiste pero no tiene que serlo tambien su tamaño  :rolleyes:


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Littlehorse en 10 Agosto 2010, 22:32 pm
Todo muy bien, pero solo tengo una duda.

Por ejemplo, si la función gets() es peligrosa e inestable, ¿Por qué sigue en el estandar?, ¿Solo por compatibilidad?

Saludos

Si, principalmente por compatibilidad con código antiguo.

Muchas otras funciones son explotables (he mencionado algunas en el primer post, no todas) pero con ellas se pueden evitar errores haciendo chequeos de los datos, manejo de memoria, chequeo de limites, etc. Es decir, a pesar que no sea recomendable utilizar funciones que sean inseguras desde su base, por lo menos existe la posibilidad de solucionarlos agregando código, aunque obviamente, ese agregado de código puede generar que el programa final sea propenso a errores, por lo menos existe la posibilidad. Lo ideal siempre sera utilizar alternativas seguras, y en la medida de lo posible, que sean estándar.

Por otro lado, con gets no hay nada que puedas hacer para evitar que sea explotable. Utilizarla es asegurarte como mínimo una posibilidad de desbordamiento de buffer. De hecho esta marcada como obsoleta en las ultimas revisiones de C99 y la mayoría de los entornos de trabajo conocidos emiten advertencias al utilizarla.

Saludos


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: rob1104 en 12 Agosto 2010, 19:25 pm
Gracias Littlehorse, nunca está de más saberlo. Aunque ahorita estoy un poco más enfocado en programación Windows en entornos RAD, es importante conocer las carencias de los lenguajes para no caer en errores básicos.

Saludos


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: someRandomCode en 7 Octubre 2010, 22:57 pm
Me quedé con lo de un post anterior.
Deprecated significa DECREPITO, no banneado.
Esas funciones están decrepitas porque otra ya indexa sus funcionalidades y ha sido favorecida por algún motivo, y como tal, el uso de la nueva y no de la decrepita esta favorecida.
Y las otras que tienen poison como pragma comment van a hacer que si le pasamos al compilador (bueno, a GCC voy a dar el ejemplo) -Wall o -pedantic nos advierta o nos tire error.
Lo cierto es que todavia son usables, estan bajo una capa de transición y como venimos haciendo eso con aplicaciones de 16 bits hasta Vista, podemos esperar que duren bastante mas ahi estando.


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Merlow en 14 Octubre 2010, 19:41 pm
Siento revivir un poco el post pero es para agradecerte la explicacion. Como bien dices yo soy uno de esos que esta ahora en la universidad y aciertas tambien en que nos han "enseñado" a incluir la libreria conio.h


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Horricreu en 14 Octubre 2010, 19:45 pm
Puedes comentar siempre que quieras en las chinchetas :D


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: rbarragan en 31 Octubre 2010, 09:15 am
Muy bien explicado, de echo en mi Universidad enseñan varios "malos habitos" para programar. Aunque soy de nivel básico. Pero pues ya con eso nos damos una idea.
Saludos.


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Uxio en 6 Noviembre 2010, 13:40 pm
En mi facultad había tenido muchos problemas con srtcat y strcopy cuando estaba aprendiendo, y no sabía por qué fallaba...

Para mirar todo este tipo de fallos y sobreescrituras en memoria usaba Valgrind, que te informa de todas las cafradas que haces y en qué líneas (si has compilado el programa con la información de depuración)


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: flacc en 19 Noviembre 2010, 01:00 am
Pero si un profesor de universidad enseña eso dando por saldado como bien hecho quiere decir que ese profe no debe enseñar hasta actualizarse, amenos que lo haga con tu mismo proposito, el de decir que no se debe hacer... es interesante, caundo me pase la primera vez por este foro era mas ignorante en informatica....saludoss


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: veco en 19 Marzo 2011, 22:02 pm
 muy bueno la voy atilizar mucho


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: [Zero] en 26 Marzo 2011, 23:50 pm
Bueno, ahora que yo también estoy en la Universidad, y que tengo que programar aplicaciones por consola me entró la duda sobre fflush(stdin). Tengo claro que no es correcto usarla, pero no tengo clara la alternativa. Algunos mencionais que en vez de utilizar scanf(), que deja basura en el buffer, se puede utilizar fgets() "que no lo hace", pero sí lo hace, si metes más caracteres que los pasados en el segundo parámetro de fgets siguen quedando cosas en el buffer.

He leído cosas como hacer esto:
Código
  1. int Opt=0;
  2. scanf("%d",&Opt);
  3. while(getchar()!='\n');
  4.  

¿Puede presentarse algún problema la hacerlo así? ¿Otras opciones?

Saludos


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Akai en 27 Marzo 2011, 10:51 am
[Zero]
Si sabes que tu lectura va a ser siempre un entero y un salto de linea, creo que esto debería dar la talla.
Código
  1. scanf("%d\n"&entero);

En principio debería funcionar, si no me equivoco en algo.

Para lectura de dos enteros, (entero espacio entero salto de linea)

Código
  1. scanf("%d %d\n"&entero)

Esto es debido a que scanf se utiliza para "scan with format".

Como comentario curioso, en una práctica teníamos que leer algo de este estilo de un fichero:

r tabulador X tabulador (45,64) espacio (78,95) salto de linea

infernal, no?
\t es el tabulador
Código
  1. fscanf(fichero, "%c\t%c\t(%d,%d) (%d,%d)\n",&char1,&char2,&int1,&int2,&int3,&int4)

y problema solucionado xD


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: [Zero] en 27 Marzo 2011, 16:39 pm
Código
  1. scanf("%d\n"&entero);

Vamos a diferenciar entre lo que debería ser y lo que puede ser xD. Yo quiero leer un entero, claro está, pero el usuario puede introducir cualquier cosa, si le digo que ponga un entero, y pone "123 abc" dejaría basura en el buffer, y es lo que quiero evitar, no dejar basura en el buffer aunque el usuario no haga caso.

Saludos

Edito: Vale, no ví el '\n' xD.

Edito2: Bueno, da igual, si el usuario introduce una cadena, la cadena quedaría en el buffer.


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: leogtz en 27 Marzo 2011, 20:30 pm
fgets + sscanf


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: [Zero] en 27 Marzo 2011, 20:37 pm
fgets + sscanf

Pero es lo que dije antes, fgets deja basura en el buffer si el usuario escribe un número de caracteres mayor que el del segundo parámetro. Le puedo poner 1000 en el segundo parámetro, pero si el usuario mete 1001 caracteres fgets deja 1 en el buffer  :P.

Saludos


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: leogtz en 27 Marzo 2011, 20:45 pm
Pero es lo que dije antes, fgets deja basura en el buffer si el usuario escribe un número de caracteres mayor que el del segundo parámetro. Le puedo poner 1000 en el segundo parámetro, pero si el usuario mete 1001 caracteres fgets deja 1 en el buffer  :P.

Saludos

¿Y para qué crees que es el sscanf()?


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: [Zero] en 27 Marzo 2011, 20:55 pm
¿Y para qué crees que es el sscanf()?

sscanf opera sobre el buffer del primer parámetro de fgets, no limpia stdin, que es el problema  :-\.

Saludos


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: leogtz en 27 Marzo 2011, 20:56 pm
Código
  1. #include <stdio.h>
  2. int main()
  3. {
  4.    unsigned int n;
  5.    char buffer[100];
  6.    do
  7.    {
  8.        printf("n : ");
  9.        fgets(buffer, 99, stdin);
  10.    } while(sscanf(buffer, "%u", &n) != 1);
  11.    printf("n : %u\n", n);
  12.    return 0;
  13. }
  14.  


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: [Zero] en 27 Marzo 2011, 21:05 pm
Código
  1. #include <stdio.h>
  2.  
  3. int main()
  4. {
  5.    unsigned int n;
  6.    char buffer[10];
  7.    do
  8.    {
  9.        printf("n : ");
  10.        fgets(buffer,9, stdin);
  11.    } while(sscanf(buffer, "%u", &n) != 1);
  12.    printf("n : %u\n", n);
  13.    getchar();
  14.    return 0;
  15. }
  16.  

Si meto un número de caracteres menor que el buffer, el getchar() funciona porque stdin está limpio, pero si meto 15 caracteres, getchar() obtiene lo que quedó en stdin sobrante y no hace lo que quería que hiciera.

Saludos


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Eternal Idol en 28 Marzo 2011, 10:20 am
¿Alguna razon para no usar un buffer de entrada de por ejemplo 120 caracteres? fgets no deja basura, a lo sumo no lee mas de lo permitido pero no es lo mismo.


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: [Zero] en 28 Marzo 2011, 16:24 pm
¿Alguna razon para no usar un buffer de entrada de por ejemplo 120 caracteres? fgets no deja basura, a lo sumo no lee mas de lo permitido pero no es lo mismo.

Bueno, pues preferiría evitar que haga eso. Si meto un buffer de 300 bytes pues al usuario no se le va a ocurrir introducir más, y si lo hace es normal que el programa no funcione correctamente, pero si se puede evitar mejor.

Saludos


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Littlehorse en 28 Marzo 2011, 19:08 pm
Mas allá de la seguridad de fgets, también es mas estable dado que su comportamiento es predecible. Para lograr lo mismo con scanf hay que jugar bastante con los parámetros, y a la larga queda poco agradable a la vista y un tanto confuso.

No hay nada en C para descartar caracteres (como si lo hay en C++) por lo que si se trata de mantener el estándar la mejor alternativa es hacer una función con el primer código que pusiste y llamarla cuando corresponda -pero cuidado, chequear \n pero también EOF-

Si realmente necesitas mas flexibilidad entonces no te queda alternativa mas que utilizar funciones especificas del sistema ya sea para limpiar el buffer o para modificar el modo de I/O.

Saludos!



Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: [Zero] en 29 Marzo 2011, 15:38 pm
Gracias, era lo que quería saber, ya no hay duda  ;D.

Saludos


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Maurice_Lupin en 14 Mayo 2011, 05:55 am
Soy nuevo en el foro, asi que si me equivoco me lo hacen saber =) . No sabia si crear un nuevo tema, en fin. Aqui comparto unas funciones que hace tiempito hice para leer en consola, intente limitar los caracteres, si tienen sugerencias se los agradeceria. Por cierto hay alguna función estandar que haga lo mismo que getch(); para no usar la libreria conio.h, este código lo compile con Dev C++ 4.9

libreria.h
Código
  1. #include<conio.h>
  2. // esta función lee caracteres limitando la cantidad de estos
  3. void ygets(char *c, register int n)
  4.  {register char car;
  5.   register int t;  
  6.   for(t=0; t<n ;) //lee hasta el maximo de caracteres
  7.      {car=getch(); // caracter por caracter
  8.       switch(car)
  9.         {// si presionaste enter, ingresa caracter nulo
  10.          // y termina el bucle
  11.          case '\r': c[t]='\0' ;return;
  12.          // si presionaste delete, retrocede, imprime espacio
  13.          // y vuelve a retroceder, da la impresión que no
  14.          // presionaste nada =)
  15.          case '\b': if(t>0) {t-- ;printf("\b \b");};
  16.          break;
  17.          // muestra lo demas y almacena en nuestra variable
  18.          default: putchar(car); c[t]=car; t++;
  19.         }  
  20.      }
  21.   c[n]='\0';  
  22.  }
  23.  
  24. // casi lo mismo quet ygets(), pero sólo
  25. // admite numeros en el switch
  26. void LeerN(char *c, int n)
  27.  {register char car;
  28.   register int t;
  29.   for(t=0;t<n;)
  30.      {car=getch();
  31.       switch(car)
  32.         {case '\r': c[t]='\0' ;return;
  33.          case '\b': if(t>0) {t-- ;printf("\b \b");}; break;
  34.          case '0':case '1':case '2':case '3':case '4':case '5':
  35.          case '6':case '7':case '8':case '9':
  36.               c[t]=car;putchar(car);t++;break;            
  37.          default: break;
  38.         }  
  39.      }
  40.   c[n]='\0';  
  41.  }
  42.  
  43. // para leer los passwords hace lo mismo que ygets()
  44. // pero muestra "*" en lugar de caracteres o.0!
  45. void leerPass(char *c, int n)
  46.  {register char car;
  47.   register int t;
  48.   for(t=0;t<n;)
  49.      {car=getch();
  50.       switch(car)
  51.         {case '\r': c[t]='\0' ;return;
  52.          case '\b': if(t>0) {t-- ;printf("\b \b");}; break;
  53.          default: {putchar('*');
  54.                   c[t]=car; t++;}
  55.         }
  56.      }
  57.   c[n]='\0';  
  58.  }
  59.  
  60.  

Código
  1. #include <stdio.h>
  2. #include <iostream>
  3. #include "libreria.h"
  4.  
  5.  
  6. int main(int argc, char *argv[])
  7. {   char t[15];  
  8.  
  9.    printf("Max 9 digitos \n");
  10.    printf("Solo numeros: "); LeerN(t,9);    
  11.    printf("\nnum: "); printf("%s \n",t);
  12.    //-----------------------
  13.    printf("\nMax 9 caracteres");    
  14.    printf("\nCualquier caracter: "); ygets(t,9);
  15.    printf("\nCadena: "); printf("%s \n",t);    
  16.    //------------------------
  17.    printf("\nMax 5 caracteres");
  18.    printf("\nLee Pass: "); leerPass(t,5);
  19.    printf("\nIngreso: "); printf("%s",t);    
  20.    getch();
  21.    return EXIT_SUCCESS;
  22. }
  23.  


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: gowend en 20 Agosto 2011, 17:59 pm
Veo que tengo que aprender mucho aun  ;-)


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Lev3rag3 en 22 Agosto 2011, 19:40 pm
Gracias por esta informacion amigo me sirvio de mucho


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Y2J en 24 Agosto 2011, 02:53 am
Vaya, al parecer tengo buenos habitos de programacion;


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: skatres en 10 Noviembre 2011, 20:26 pm
Gracias por los consejos publicados, sin embargo no entiendo como en las universidades , incitan al uso de funciones como <conio.h> o system("pause")


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Shamaroot en 28 Noviembre 2011, 04:13 am
Gracias, me servira de bastante :)

Saludos !!


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Hackmundy en 25 Diciembre 2011, 07:45 am
Wua tremendo aporte yo que estoy aprendiendo c++ gracias me es muy util te lo agradezco..
Leyendo.....

Saludos..


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: TheTooxicoo en 1 Septiembre 2012, 16:30 pm
Muy bueno Littlehorse. Magnífico aporte que esperemos que sigas mejorando.


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Skeletron en 6 Septiembre 2012, 17:53 pm
Muy rico todo, pero no me quedo claro cuales son las buenas formas de hacerlo.


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: zonahurbana en 30 Septiembre 2012, 05:18 am
1. Entiendo, por nada del mundo debo usar gets porque no tiene control
interno (se desborda cuando se ingresa más de lo debido). En vez de ello
se indica que es preferible usar fgets, pero es en C, ¿y en C++?
Es decir, como pertenece a C, según me explicaron, también puede usarse
en un compilador de C++, pero quizás no sea recomendable usar las
funciones antiguas con las nuevas que trae C++, ¿o normal?
Me parece que puedo usar cin.getline(arreglo, 5) // Como parámetros el
nombre del arreglo y la dimensión límite. Y si se ingresa menos, ¿se
llena con '\n'?

2. Se dice que scanf deja basura por doquier, ¿lo mismo ocurre con cin>>?

3. Se recomienda usar cin.get(); en vez de system("pause"); ¿Pero es que
cin.get(); no consistía en leer un caracter?

4. Nunca he usado la librería conio, y si argumentan que no es necesario
incluirla, creo que jamás lo haré. Pero tengo una duda. He visto que
muchos evitan usar using namespace std; y sólo ponen algo como std::cout
o std::cin al usar estas funciones.

5. Entonces lo correcto es int main(void){}; ¿verdad? Ya me han explicado
que el int _tmain(...){} del Visual Studio es algo inherente a ese IDE,
pero que según el estándar se usa lo citado inicialmente. Pero, ¿por qué
es que se complican agregando "stdafx.h" como librería? y, ¿por qué
aparecen parámetros en el int _tmain como argc o argv?


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: 7hongo7 en 2 Mayo 2013, 17:40 pm
Muchas gracias esta información :o :o :o :o me va a servir bastante porque apenas ando introduciéndome en el mundo de la programación muchas gracias hermano!!!!!!!! ;-) ;-) ;-) ;-) ;-) ;-)


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: _Zer0_ en 21 Julio 2013, 04:04 am
Muy bien explicado, yo estoy empezando con C y una de las cosas que he aprendido de este post es que no todo es blanco o negro si no que que tienen pro y contras.

Sobre conio me a venido muy bien por que la verdad que en todo los tutoriales viene que hay que utilizarla.


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: skan en 13 Agosto 2013, 16:34 pm
Hola

Había leído esto hace un tiempo en el libro "effective c++" y algún otro sitio.
Hoy he querido consultar otra vez este tipo de comandos (como fgets o strncpy) pero no aparecen en libros modernos como Prata 6th edition o Professional C++.
¿Quiere eso decir que ya no son necesarios?
Lo digo porque get, strcpy...   aceptan también un  parámetro especificando el número de caracteres. ¿Qué ventaja hay ahora por ejemplo entre fgets y get?

saludos


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: fakundo (sylas) en 6 Enero 2014, 18:02 pm
Buen post aun que yo no me entero mucho pero comprendo algo pero es normal jeje aun hago la eso


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: DéstroReflex en 17 Febrero 2014, 06:06 am
Gracias por la explicación, son el tipo de cosas que te enseñan "por defecto" pero no tienes ni idea de que función realmente cumplen y qué podrían afectar.


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Peregring-lk en 23 Mayo 2015, 00:14 am
Nota: Respecto a los ejemplos que voy a poner aquí: ¡¡no lo hagáis en casa!! Son prácticas permitidas pero ¡antigüas, deprecated e incluso mal vistas!

Respecto al `void main()` que se cita en la guía, si programas en C++, un compilador de C++ decente te dirá que es un error de compilación y no te dejará continuar. No es que sea una mala costumbre, es que es un programa con un error sintáctico.

Sin embargo, si `main` tiene tipo `int main()`, pero no escribes un `return`, no te dará ningún error de compilación porque el estándar dice que el compilador debe añadir al final del `main` un `return 0` si no lo tiene ya. Y los compiladores obedecen al estándar como buenos esclavos.

En C, sin embargo, la cosa es aún más permisiva. Puedes no escribir el `return`, y el compilador lo añadirá. Puedes hacer que su tipo de retorno sea `void`, y el compilador también añadirá el `return 0`: ¿pero eso no puede ser? ¿un `void` no puede devolver un `int`? Ya, pero es que ese `return` se añade a nivel de ensamblador, y en ensamblador no existen tipos.

Incluso en C puedes no escribir tipo de retorno:

Código
  1. main() {}

El compilador solo te lanzará un warning diciendo que el tipo de retorno por defecto es `int` (y también te añadirá el `return 0`). De hecho, en C puedes declarar cualquier cosa sin tipo (tipos de retorno, parámetros o variables; todas serán por defecto `int`, eso sí, con warnings por doquier):

Código
  1. void f(a) {}

Y `a` tendrá por defecto tipo `int` (con su correspondiente warning). Y llevando al extremo las cosas raras, ésto también es C estándar (se permite por retrocompatibilidad con versiones antigüas de C):

Código
  1. void f(a) int a;
  2. {}

Aquí, el tipo de `a` se declara a posteriori, entre el nombre de la funcion y su cuerpo. No hace falta decir que todo ésto no se puede hacer en C++ (un compilador de C++ gritará como una loca).

Y repito, no hagáis nada de ésto en casa si no queréis que mate a un gatito.





Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Seyro97 en 30 Mayo 2015, 16:47 pm
Otra es no usar namespaces (sobre todo cuando solo vas a usar cout xD). Pongo un ejemplo:

Código 'malo':

Código
  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. int main() {
  6. cout << "HOLA" << endl;
  7.  
  8. cin.get();
  9. return 0;
  10. }

Código 'bueno':

Código
  1. #include <iostream>
  2.  
  3. int main() {
  4. std::cout << "HOLA" << std::endl;
  5.  
  6. std::cin.get();
  7. return 0;
  8. }


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: engel lex en 30 Mayo 2015, 17:16 pm
Otra es no usar namespaces (sobre todo cuando solo vas a usar cout xD). Pongo un ejemplo:

Código 'malo':

Código
  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. int main() {
  6. cout << "HOLA" << endl;
  7.  
  8. cin.get();
  9. return 0;
  10. }

Código 'bueno':

Código
  1. #include <iostream>
  2.  
  3. int main() {
  4. std::cout << "HOLA" << std::endl;
  5.  
  6. std::cin.get();
  7. return 0;
  8. }

explicate las razones


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Stakewinner00 en 30 Mayo 2015, 19:32 pm
Eso de los namespace si que esta prácticamente prohibido al programar librerías , porque el código de la librería podría entrar en conflicto con la del usuario. Pero no entiendo porque un programa sencillo no se puede usar eso...


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Peregring-lk en 30 Mayo 2015, 21:56 pm
Eso de los namespace si que esta prácticamente prohibido al programar librerías , porque el código de la librería podría entrar en conflicto con la del usuario. Pero no entiendo porque un programa sencillo no se puede usar eso...

De hecho, como bien dices, yo lo uso en código de usuario, y si hubiese un conflicto con una librería que no utilice yo directamente (librería utiliza indirectamente por las librerías que yo he añadido conscientemente), siempre puedo "evitar el conflicto con `::mivariable`, para indicar que la variable `mivariable` está en el espacio de nombres global.

En código de librería también estoy de acuerdo contigo en que no se debería usar, pero con un matiz: sólo en ficheros de cabecera. En ficheros de implementación (los `.cpp` de la librería), sí que creo que se podría utilizar sin problemas.

Aunque yo no domino mucho los matices de los espacios de nombres, he visto que muchas librerías importantes (sí, a veces me enfado tanto con los errores de ejecución que a veces me embarco en la horrorosa aventura de mirar el código fuente de una librería), utilizan nombres de espacio anónimos en los `.cpp`, porque los espacios de nombres anónimos hacen que todos los símbolos en él tenga enlazado estático, lo que significa que desde otras unidades de compilación no se puedan acceder a ellos:

Código
  1. // libreria.hpp
  2. namespace libreria {
  3.  
  4.    class loquesea {
  5.    public:
  6.         void fun1();
  7.         void fun2();
  8.    };
  9. }
  10.  
  11. // libreria.cpp
  12. #include "libreria.hpp"
  13.  
  14. namespace {
  15.   using namespace std; // Al fin y al cabo, ahora el usuario es el implementador de la librería.
  16.  
  17.   // Clases y funciones auxiliares que realmente hacen el trabajo.
  18. }
  19.  
  20. namespace libreria {
  21.  
  22.    void loquesea::fun1() { /* Implementacion utilizando las clases/funciones auxiliares */ }
  23.    void loquesea::fun1() { /* Implementacion utilizando las clases/funciones auxiliares */ }
  24.  
  25. }

De ésta forma, todo aquello que vaya más allá de la interfaz pública con el usuario, es innacesible desde fuera, debido a que los símbolos del espacio de nombres anónimo tienen enlazado estático, y por tanto, pertenecen solamente a "ésta unidad" de compilación, como cuando declaras una función externa "static". Incluso los símbolos de la `std`, como se han "introducido" dentro de un espacio de nombres anónimo, desde fuera tampoco están visibles, y el usuario final (a no ser que escriba `using namespace std` en su código), no tendrá conflictos ni con la librería, ni con la `std`.

Es una forma se reduce muchísimo la cantidad de símbolos que el enlazador tiene que buscar.


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Seyro97 en 12 Junio 2015, 03:20 am
Eso de los namespace si que esta prácticamente prohibido al programar librerías , porque el código de la librería podría entrar en conflicto con la del usuario. Pero no entiendo porque un programa sencillo no se puede usar eso...


Era un ejemplo :P


Título: Re: |Lo que no hay que hacer en C/C++. Nivel basico|
Publicado por: Seyro97 en 24 Octubre 2015, 08:48 am
explicate las razones

Casi nunca miro este tema xDD. Siento no haber respondido antes. Las razones son una tontería, pero ya que C++ nos provee una estructura lógica, pues vamos a usarla... Vamos, eso pienso yo. Otra razón, aunque casi ni es razón, imaginemos que una librería que no conoce cout (? jaja), y usa cout para crear una clase, pues entonces habría un conflicto xD