(2)Sobrepasando límites de un arreglo. (3)Perdiendo la condición base de una funcion recursiva. (4)Usando constantes de caracteres en vez de literales de cadena y viceversa. (5)Literales de tipo float son por defecto de tipo double. (6)Olvidando liberar la memoria. (7)Añadiendo un semicolon a #define. (8)No ser cuidadoso con el semicolon. (9)Erroneamente escribir = o ==. (10)Copiando demasiado. (11)Las macros son solo reeemplazo de cadenas. 1.- Mezclar enteros signed y unsigned en operaciones aritmeticas
#include stdio.h
int main(void){ unsigned int a = 1000;signed int b = -1; if (a > b) puts("a is more than b"); else puts("a is less or equal than b"); return 0;}
Como 1000 es mayor que -1 uno esperaría que a es mayor que b. Antes de hacer la comparación, b es convertida a
unsigned int. Cuando es convertido a unsigned int toma el valor máximo de unsigned int... el cual es mayor que 1000. Por esto se puede apreciar que a > b es una falso.
int main(void){ unsigned int a = 1000;signed int b = -1; if (a > b) puts("a is more than b"); else puts("a is less or equal than b"); return 0;}
2.- Sobrepasando los límites de un arreglo. Los arreglos siempre empiezan en 0 y terminan en la longitud del arreglo - 1.
#include stdio.h
int main(){ int x = 0;int myArray[5] = {1,2,3,4,5}; for(x=1; x<=5; x++){printf("%d\t",myArray[x]);} printf("\n");return 0;} //Output: 2 3 4 5 GarbageValue
La forma correcta:
int main(){ int x = 0;int myArray[5] = {1,2,3,4,5}; for(x=1; x<=5; x++){printf("%d\t",myArray[x]);} printf("\n");return 0;} //Output: 2 3 4 5 GarbageValue
#include stdio.h
int main(){ int x = 0;int myArray[5] = {1,2,3,4,5}; for(x=0; x<5; x++){printf("%d\t",myArray[x]);} printf("\n");return 0;} //Output: 1 2 3 4 5
Entonces, hay que conocer el límite de nuestros arreglos porque osino podemos corromper el buffer o provocar un fallo de segmentacion por acceder a un área de memoria distinta.
int main(){ int x = 0;int myArray[5] = {1,2,3,4,5}; for(x=0; x<5; x++){printf("%d\t",myArray[x]);} printf("\n");return 0;} //Output: 1 2 3 4 5
3.- Perdiendo la condición base en una función recursiva. Calcular la factorización de un número es un ejemplo clásico de recursividad:
#include stdio.h
int factorial(int n){return n * factorial(n - 1);}
int main(){ printf("Factorial %d = %d\n", 3, factorial(3));return 0;} //Typical output: Segmentation fault
El problema con esta función es que va a estar en un ciclo infinito, lo que causará fallo de segmentación. Necesita una condición base para detener la recursividad.
La forma correcta:
int factorial(int n){return n * factorial(n - 1);}
int main(){ printf("Factorial %d = %d\n", 3, factorial(3));return 0;} //Typical output: Segmentation fault
#include stdio.h
int factorial(int n){ // Base Condition, very crucial in designing the recursive functions.
if (n == 1){return 1;}else{return n * factorial(n - 1);}}
int main(){printf("Factorial %d = %d\n", 3, factorial(3));return 0;}
//Esta función va a terminar tan pronto alcance 1. //Output : Factorial 3 = 6
int factorial(int n){ // Base Condition, very crucial in designing the recursive functions.
if (n == 1){return 1;}else{return n * factorial(n - 1);}}
int main(){printf("Factorial %d = %d\n", 3, factorial(3));return 0;}
//Esta función va a terminar tan pronto alcance 1. //Output : Factorial 3 = 6
Reglas a seguir:
1 Iniciar el algoritmo. Las funciones recursivas necesitan con frecuencia un valor inicial con el que empezar. Esto es acompañado sea por un parametro de la función o una función puerta que no es recursiva pero pone los valores iniciales para la recursión.
2 Revisar para ver si los valores actuales que se están procesando coinciden con el caso base.Si es asi, entonces procesa y devuelve un valor
3 Redefine la respuesta en terminos de un problema pequeño o simple subproblema o subproblemas.
4 Ejecuta el algoritmo en un subproblema. 5 Combina los resultados en la formulación de la respuesta. 6 Retorna los resultados.
1 Iniciar el algoritmo. Las funciones recursivas necesitan con frecuencia un valor inicial con el que empezar. Esto es acompañado sea por un parametro de la función o una función puerta que no es recursiva pero pone los valores iniciales para la recursión.
2 Revisar para ver si los valores actuales que se están procesando coinciden con el caso base.Si es asi, entonces procesa y devuelve un valor
3 Redefine la respuesta en terminos de un problema pequeño o simple subproblema o subproblemas.
4 Ejecuta el algoritmo en un subproblema. 5 Combina los resultados en la formulación de la respuesta. 6 Retorna los resultados.
4.- Usando constantes de caracteres en vez de literales de cadena y viceversa. En lenguaje C, las cadenas de caracteres y literales de cadena son cosas distintas. 'a' ..esto es una cadena de caracter. Una cadena de caracter es de tipo entero que tiene asignado un número para ese caracter. "asdf"... es un literal de caracteres. Un literal de caracteres un arreglo inmodificable cuyos elementos son de tipo char. "asdf" tiene 5 caracteres, porque el caracter final es un \0. Este caracter final es conocido como carácter nulo. {a,s,d,f,\0}.
//ejemplo 1:
una cadena de caracteres es usada donde debería ir una literal de cadena. Esto da un comportamiento indefinido.
#include stdio.h
int main(void){ const char *hello = 'hello, world'; /* bad */puts(hello);return 0;}
//ejemplo 2: un literal de cadena se usa donde se debería usar una cadena de caracteres. El resultado es una cosa sin sentido.
#include stdio.h int main(void) { char c = "a"; /* bad */ printf("%c\n", c); return 0;}
En ambos casos el compilador se va a quejar de la mezcla. Si no pasa esto, necesita usar más advertencias para la compilación, o derechamente usar un mejor compilador.
#include stdio.h
int main(void){ const char *hello = 'hello, world'; /* bad */puts(hello);return 0;}
//ejemplo 2: un literal de cadena se usa donde se debería usar una cadena de caracteres. El resultado es una cosa sin sentido.
#include stdio.h int main(void) { char c = "a"; /* bad */ printf("%c\n", c); return 0;}
5.- Literales de tipo float son por defecto de tipo double. Hay que tener cuidado al inicializar una variable float a valores literales o compararlas con estos. Esto es debido a que literales float de valor 0.1 por lo regular son de tipo double. Este tipo de cosas nos puede conducir a sorpresas:
#include stdio.h
int main(){ float n = 0.1; if (n > 0.1) printf("Wierd\n");return 0;} // Prints "Wierd" when n is float
6.- Olvidando liberar la memoria. Uno siempre debe recordar liberar la memoria alojada, sea una función hecha por tí o por una función de librería llamada por tu función.
#include stdlib.h
#include stdio.h
int main(void){ char *line = NULL;size_t size = 0;
/* memory implicitly allocated in getline */ getline(&line, &size, stdin);
/* uncomment the line below to correct the code */ /* free(line); */return 0;}
Es un error inocente en este ejemplo específico,porque cuando un proceso termina,la mayoría de los sistemas operativos libera la memoria alojada en vez de tí.
int main(void){ char *line = NULL;size_t size = 0;
/* memory implicitly allocated in getline */ getline(&line, &size, stdin);
/* uncomment the line below to correct the code */ /* free(line); */return 0;}
7.- Añadiendo un semicolon a #define Muchas veces me pasó a mí!!! Es fácil confundirse con el preprocesador de C, y tratarlo como parte del lenguaje. Pero es un error, porque el preprocesador es sólo un mecanismo de reemplazo de texto.
// WRONG
#define MAX 100;
int arr[MAX]={0};
//lo cual se traduce como int arr[100]={0}; //Que se traduce como error de sintaxis.
8.- Ser cuidadoso con el semicolon
//lo cual se traduce como int arr[100]={0}; //Que se traduce como error de sintaxis.
//Esto:
if (x > a);
a = x;
//Significa esto: if (x > a) {} a = x;
Aveces, perder un semicolon puede provocar problemas inesperados:
//Significa esto: if (x > a) {} a = x;
if (i < 0)
return
day = date[0];
hour = date[1];
minute = date[2];
/*El semicolon antes de return está perdido, por lo que day =date[0]; va a ser regresado. El compilador lee hasta que encuentra ; como el fin de línea.*/
9.- Erroneamente escribir = en vez de ==.
El = es para asignar.
El == es para COMPARAR.
A veces hacemos: /* assign y to x */ if (x = y) {/* logic */}
//cuando lo que uno queria era: /* compare if x is equal to y */ if (x == y) {/* logic */}
//lo cual es equivalente a: /* compare if x is equal to y */ if (x == y) != 0{/* logic */}
A veces hacemos: /* assign y to x */ if (x = y) {/* logic */}
//cuando lo que uno queria era: /* compare if x is equal to y */ if (x == y) {/* logic */}
//lo cual es equivalente a: /* compare if x is equal to y */ if (x == y) != 0{/* logic */}
10.- Copiando demasiado.
char buf[8]={0}; /* tiny buffer, easy to overflow */
printf("What is your name?\n"); scanf("%s", buf); /* WRONG */ scanf("%7s", buf); /* RIGHT */
Si uno pone más caracteres que los requeridos por scanf, van a empezar a sobreescribirse zonas de memorias aledañas al buffer. Esto puede derivar en comportamiento indefinido. Los hackers maliciosos con frecuencia usan esto para sobreeescribir la dirección de return, y cambiar la dirección por la del código malicioso creado por este.
printf("What is your name?\n"); scanf("%s", buf); /* WRONG */ scanf("%7s", buf); /* RIGHT */
11.- Las Macros son solo reemplazo de cadenas
#include stdio.h
#define SQUARE(x) x*x
//este es el error. int main(void){printf("%d\n", SQUARE(1+2));return 0;}
Esperarías que este código devolviese 9, pero devolverá 5 porque la macro será expandida a 1+2*1+2. Para evadir este problema debe encerrar entre () las fichas para evadir este problema. #include stdio.h
#define SQUARE(x) ((x)*(x))
int main(void){printf("%d\n", SQUARE(1+2));return 0;}
//este es el error. int main(void){printf("%d\n", SQUARE(1+2));return 0;}
Esperarías que este código devolviese 9, pero devolverá 5 porque la macro será expandida a 1+2*1+2. Para evadir este problema debe encerrar entre () las fichas para evadir este problema. #include stdio.h
#define SQUARE(x) ((x)*(x))
int main(void){printf("%d\n", SQUARE(1+2));return 0;}