Primeras prácticas básicas
Continuando con el manual de C, nos indica cómo hacer aritmética básica mostrándonos un programa en donde se convierten valores de Fahrenheit a Celcius. Decidí leer el código con detalle y hacer la implementación desde 0 por mi propia cuenta sin copiar la del libro directamente:
openjazz$ vim main.c
#include <stdio.h>
int ftoc(int f)
{
int c = (f - 32) / 1.8;
return c;
}
int main()
{
printf("De Fahrenhet a Celcius\n");
int fahrenheit = 0;
int paso = 10;
int upper = 300;
int lower = -300;
int celcius = 0;
for(fahrenheit=lower; fahrenheit <= upper; fahrenheit = fahrenheit + paso)
{
celcius = ftoc(fahrenheit);
printf("Fahrenheit: %d Celcius: %d\n", fahrenheit, celcius);
}
return 0;
}
openjazz$ clang main.c
openjazz$ ./
a.out main.c
openjazz$ ./a.out
De Fahrenhet a Celcius
Fahrenheit: -300 Celcius: -184
Fahrenheit: -290 Celcius: -178
Fahrenheit: -280 Celcius: -173
Fahrenheit: -270 Celcius: -167
Fahrenheit: -260 Celcius: -162
Fahrenheit: -250 Celcius: -156
Fahrenheit: -240 Celcius: -151
Fahrenheit: -230 Celcius: -145
Fahrenheit: -220 Celcius: -140
Fahrenheit: -210 Celcius: -134
Fahrenheit: -200 Celcius: -128
Fahrenheit: -190 Celcius: -123
Fahrenheit: -180 Celcius: -117
Fahrenheit: -170 Celcius: -112
Fahrenheit: -160 Celcius: -106
Fahrenheit: -150 Celcius: -101
Fahrenheit: -140 Celcius: -95
Fahrenheit: -130 Celcius: -90
Fahrenheit: -120 Celcius: -84
Fahrenheit: -110 Celcius: -78
Fahrenheit: -100 Celcius: -73
Fahrenheit: -90 Celcius: -67
Fahrenheit: -80 Celcius: -62
Fahrenheit: -70 Celcius: -56
Fahrenheit: -60 Celcius: -51
Fahrenheit: -50 Celcius: -45
Fahrenheit: -40 Celcius: -40
Fahrenheit: -30 Celcius: -34
Fahrenheit: -20 Celcius: -28
Fahrenheit: -10 Celcius: -23
Fahrenheit: 0 Celcius: -17
Fahrenheit: 10 Celcius: -12
Fahrenheit: 20 Celcius: -6
Fahrenheit: 30 Celcius: -1
Fahrenheit: 40 Celcius: 4
Fahrenheit: 50 Celcius: 10
Fahrenheit: 60 Celcius: 15
Fahrenheit: 70 Celcius: 21
Fahrenheit: 80 Celcius: 26
Fahrenheit: 90 Celcius: 32
Fahrenheit: 100 Celcius: 37
Fahrenheit: 110 Celcius: 43
Fahrenheit: 120 Celcius: 48
Fahrenheit: 130 Celcius: 54
Fahrenheit: 140 Celcius: 60
Fahrenheit: 150 Celcius: 65
Fahrenheit: 160 Celcius: 71
Fahrenheit: 170 Celcius: 76
Fahrenheit: 180 Celcius: 82
Fahrenheit: 190 Celcius: 87
Fahrenheit: 200 Celcius: 93
Fahrenheit: 210 Celcius: 98
Fahrenheit: 220 Celcius: 104
Fahrenheit: 230 Celcius: 110
Fahrenheit: 240 Celcius: 115
Fahrenheit: 250 Celcius: 121
Fahrenheit: 260 Celcius: 126
Fahrenheit: 270 Celcius: 132
Fahrenheit: 280 Celcius: 137
Fahrenheit: 290 Celcius: 143
Fahrenheit: 300 Celcius: 148
Lo más interesante que ví, viniendo de Java, es la manera en la que se concatenan las cadenas de texto y las variables. El libro
nos muestra el siguiente ejemplo de concatenación: printf("%3.0f %6.1f\n", fahr, celsius); y también nos deja esta
tabla la cuál será un obvia referencia para el futuro ya que planeo imprimir textos de manera constante:
%d print as decimal integer
%6d print as decimal integer, at least 6 characters wide
%f print as floating point
%6f print as floating point, at least 6 characters wide
%.2f print as floating point, 2 characters after decimal point
%6.2f print as floating point, at least 6 wide and 2 after decimal point
Si van siguiendo el manual, se darán cuenta de que me adelanté a algunos pasos como el uso de funciones y el for
y esque hay ciertas cosas que son casi iguales en Java, así que decidí implementarlas de una vez. Resaltaré principalmente el
conocimiento que sea muy nuevo para mí.
Constantes simbólicas
Este es un concepto nuevo para mí. Al parecer en C, se pueden declarar una especie de constantes, fuera de cualquier función,
lo que me hace suponer que estas variables son accesibles desde cualquier función. Para practicarlas, decidí reemplazar las
variables que tiene mi implementación por constantes simbólicas. Agregué la variable n solo para poder llevar la
cuenta del for.
#include <stdio.h>
#define PASO 10
#define UPPER 300
#define LOWER -300
int ftoc(int f)
{
int c = (f - 32) / 1.8;
return c;
}
int main()
{
printf("De Fahrenhet a Celcius\n");
int fahrenheit = 0;
/*
int paso = 10;
int upper = 300;
int lower = -300;
*/
int celcius = 0;
int n = 0;
for(fahrenheit=LOWER; fahrenheit <= UPPER; fahrenheit = fahrenheit + PASO)
{
celcius = ftoc(fahrenheit);
printf("N: %d Fahrenheit: %d Celcius: %d\n", n, fahrenheit, celcius);
n = n+1;
}
return 0;
}
openjazz$ clang main.c
openjazz$ ./a.out
De Fahrenhet a Celcius
N: 0 Fahrenheit: -300 Celcius: -184
N: 1 Fahrenheit: -290 Celcius: -178
N: 2 Fahrenheit: -280 Celcius: -173
N: 3 Fahrenheit: -270 Celcius: -167
N: 4 Fahrenheit: -260 Celcius: -162
N: 5 Fahrenheit: -250 Celcius: -156
N: 6 Fahrenheit: -240 Celcius: -151
N: 7 Fahrenheit: -230 Celcius: -145
N: 8 Fahrenheit: -220 Celcius: -140
N: 9 Fahrenheit: -210 Celcius: -134
N: 10 Fahrenheit: -200 Celcius: -128
N: 11 Fahrenheit: -190 Celcius: -123
N: 12 Fahrenheit: -180 Celcius: -117
N: 13 Fahrenheit: -170 Celcius: -112
N: 14 Fahrenheit: -160 Celcius: -106
N: 15 Fahrenheit: -150 Celcius: -101
N: 16 Fahrenheit: -140 Celcius: -95
N: 17 Fahrenheit: -130 Celcius: -90
N: 18 Fahrenheit: -120 Celcius: -84
N: 19 Fahrenheit: -110 Celcius: -78
N: 20 Fahrenheit: -100 Celcius: -73
N: 21 Fahrenheit: -90 Celcius: -67
N: 22 Fahrenheit: -80 Celcius: -62
N: 23 Fahrenheit: -70 Celcius: -56
N: 24 Fahrenheit: -60 Celcius: -51
N: 25 Fahrenheit: -50 Celcius: -45
N: 26 Fahrenheit: -40 Celcius: -40
N: 27 Fahrenheit: -30 Celcius: -34
N: 28 Fahrenheit: -20 Celcius: -28
N: 29 Fahrenheit: -10 Celcius: -23
N: 30 Fahrenheit: 0 Celcius: -17
N: 31 Fahrenheit: 10 Celcius: -12
N: 32 Fahrenheit: 20 Celcius: -6
N: 33 Fahrenheit: 30 Celcius: -1
N: 34 Fahrenheit: 40 Celcius: 4
N: 35 Fahrenheit: 50 Celcius: 10
N: 36 Fahrenheit: 60 Celcius: 15
N: 37 Fahrenheit: 70 Celcius: 21
N: 38 Fahrenheit: 80 Celcius: 26
N: 39 Fahrenheit: 90 Celcius: 32
N: 40 Fahrenheit: 100 Celcius: 37
N: 41 Fahrenheit: 110 Celcius: 43
N: 42 Fahrenheit: 120 Celcius: 48
N: 43 Fahrenheit: 130 Celcius: 54
N: 44 Fahrenheit: 140 Celcius: 60
N: 45 Fahrenheit: 150 Celcius: 65
N: 46 Fahrenheit: 160 Celcius: 71
N: 47 Fahrenheit: 170 Celcius: 76
N: 48 Fahrenheit: 180 Celcius: 82
N: 49 Fahrenheit: 190 Celcius: 87
N: 50 Fahrenheit: 200 Celcius: 93
N: 51 Fahrenheit: 210 Celcius: 98
N: 52 Fahrenheit: 220 Celcius: 104
N: 53 Fahrenheit: 230 Celcius: 110
N: 54 Fahrenheit: 240 Celcius: 115
N: 55 Fahrenheit: 250 Celcius: 121
N: 56 Fahrenheit: 260 Celcius: 126
N: 57 Fahrenheit: 270 Celcius: 132
N: 58 Fahrenheit: 280 Celcius: 137
N: 59 Fahrenheit: 290 Celcius: 143
N: 60 Fahrenheit: 300 Celcius: 148
Entradas y salidas
En esta sección se nos explica cómo interactuar con el usuario. Según lo que he investigado, aquí es donde principalmente
C se comienza a volver más inseguro, ya que el manejo correcto de entradas y salidas queda por completo en nuestras manos
e incluso los errores relaciones con escrituras incorrectas en memoria. De momento como aún estoy aprendiendo este lenguaje,
haré todo lo más sencillo y entendible posible para mí, siguiendo lo que diga el manual y después es cuando me pondré más en
la labor de reestructurar lo que aprenda de acuerdo al referente que elegimos, el cuál es OpenBSD.
Voy a crear un nuevo archivo para poder tener el otro a mano. El nuevo se llamará, interaccion.c.
La página 18 del manual de C, nos explica 2 funciones nuevas getchar() y putchar(). Luego nos da un
ejemplo en donde se usan las dos funciones. Voy a implementarlo a mano y veremos lo que obtenemos.
openjazz$ vim interaccion.c
#include <stdio.h>
int main(void)
{
int c;
c = getchar();
while(c != EOF)
{
putchar(c);
c = getchar();
}
}
openjazz$ clang interaccion.c
openjazz$ ./a.out
0
0
1
1
a
a
2
2
aa
aa
test
test
$
$
^C
openjazz$
Este programa parece que simplemente acepta una entrada, la recibe y luego la imprime. Parece que ambas funciones
getchar() y putchar() solo aceptan un solo char pero procesan la entrada carácter por
carácter hasta que llega algo llamado EOF lo cuál parece ser un indicador especial que le dice al
programa que llegó al último carácter del string.
El manual nos indica que podemos simplificar el código poniendo getchar() dentro de la expresión
while de esta manera: while ((c = getchar()) != EOF) ya que getchar() devuelve
el valor que sigue en la cade al llamarlo.
En la página 20 nos muestran como utilizar getchar() para contar la cantidad de carácteres de entrada.
El manual nos muestra una implementación pero intentaré hacerla a mano por mi propia cuenta para practicar.
openjazz$ vim contar-char.c
#include <stdio.h>
int main(void)
{
int count = 0;
while(getchar() != EOF)
{
count = count + 1;
printf("Counting... %d\n", count);
}
printf("Char total count: %d\n", count);
}
openjazz$ clang contar-char.c
openjazz$ ./a.out
saigbsidfhybgudshfbg
Counting... 1
Counting... 2
Counting... 3
Counting... 4
Counting... 5
Counting... 6
Counting... 7
Counting... 8
Counting... 9
Counting... 10
Counting... 11
Counting... 12
Counting... 13
Counting... 14
Counting... 15
Counting... 16
Counting... 17
Counting... 18
Counting... 19
Counting... 20
Counting... 21
Char total count: 21
El siguiente ejercicio en el libro es contar líneas. Denuevo, intentaré hacer mi propia implementación, solo tomando de referencia el ejemplo del libro si se me dificulta alguna parte.
openjazz$ vim contar-lines.c
#include <stdio.h>
int main(void)
{
int count = 0;
char c = '0';
while(c != EOF)
{
c = getchar();
if(c == '\n')
{
count = count + 1;
printf("Counting... %d\n", count);
}
}
printf("Total count: %d\n", count);
}
openjazz$ clang contar-lines.c
openjazz$ ./a.out
12312312
Counting... 1
Total count: 1
openjazz$ ./a.out
aesfsdf^[[27;2;13~
Counting... 1
asdsdf
Counting... 2
Total count: 2
Seguidamente el manual muestra un ejemplo más complejo. Intentaré hacer mi versión en donde detalle bastante todo lo que entra.
openjazz$ vim contar-todo.c
#include <stdio.h>
int main(void)
{
int charcount = 0;
int linecount = 0;
int wordcount = 0;
char c = '0';
char p = '0';
while(c != EOF)
{
c = getchar();
if((c == ' ' || c == '\n') && p != ' ')
{
wordcount = wordcount + 1;
}
if(c == '\n')
{
linecount = linecount + 1;
}
charcount = charcount + 1;
p = c;
}
printf("Char Count: %d\nLine Count: %d\nWord Count: %d\n", charcount, linecount, wordcount);
}
openjazz$ clang contar-todo.c
openjazz$ ./a.out
Hola, esta es una
prueba para saber
si mi programa
funciona correctamente
Char Count: 75
Line Count: 4
Word Count: 13
He de admitir que la solución del libro es mucho más impresionante, pero intenté hacer la mía de la manera más entendible y corta posible para no complicarme demasiado. Creo que vale la pena que muestre la del libro:
#include <stdio.h>
#define IN 1 /* inside a word */
#define OUT 0 /* outside a word */
/* count lines, words, and characters in input */
main()
{
int c, nl, nw, nc, state;
state = OUT;
nl = nw = nc = 0;
while ((c = getchar()) != EOF) {
++nc;
if (c == '\n')
++nl;
if (c == ' ' || c == '\n' || c = '\t')
state = OUT;
else if (state == OUT) {
state = IN;
++nw;
}
}
printf("%d %d %d\n", nl, nw, nc);
}
Arrays
En esta parte el libro se pone un poco más exigente ya que nos introduce un programa que usa arrays
directamente sin explicar lo básico de un array. Esto me da a entender que este libro no es para principiantes
en la programación. La verdad tiene sentido tomando en cuenta el objetivo que tiene C como lenguaje, las personas
para las que va dirigido y principalmente el hecho de que este es un libro sobre C y no sobre programación
básica en general.
A continuación, el manual nos indica la creación de un porgrama que utilice un array para sacar cuentas con respecto
al input.
En mi caso, voy a crear una implementación por mi propia cuenta que cuente cuantas veces sale cada dígito, los
espacios en blanco, los saltos de línea, las tabulaciones y otros caracteres.
openjazz$ vim array1.c
#include <stdio.h>
int main (void)
{
int count[14] = {0};
char c = '0';
while((c = getchar()) != EOF)
{
switch(c)
{
case '0':
++count[0];
break;
case '1':
++count[1];
break;
case '2':
++count[2];
break;
case '3':
++count[3];
break;
case '4':
++count[4];
break;
case '5':
++count[5];
break;
case '6':
++count[6];
break;
case '7':
++count[7];
break;
case '8':
++count[8];
break;
case '9':
++count[9];
break;
case ' ':
++count[10];
break;
case '\t':
++count[11];
break;
case '\n':
++count[12];
break;
default:
++count[13];
}
}
printf("Veces que aparece cada tipo de caracter\n");
printf("Numero 0: %d\n", count[0]);
printf("Numero 1: %d\n", count[1]);
printf("Numero 2: %d\n", count[2]);
printf("Numero 3: %d\n", count[3]);
printf("Numero 4: %d\n", count[4]);
printf("Numero 5: %d\n", count[5]);
printf("Numero 6: %d\n", count[6]);
printf("Numero 7: %d\n", count[7]);
printf("Numero 8: %d\n", count[8]);
printf("Numero 9: %d\n", count[9]);
printf("Espacios: %d\n", count[10]);
printf("Tabulado: %d\n", count[11]);
printf("Saltos linea: %d\n", count[12]);
printf("Otros: %d\n", count[13]);
return 0;
}
openjazz$ clang array1.c
openjazz$ ./a
a.out array1.c
openjazz$ ./a.out
est0 es una 77777 prueba
tabulacion88888
cierre 333333 000 4^D
Veces que aparece cada tipo de caracter
Numero 0: 4
Numero 1: 1
Numero 2: 1
Numero 3: 7
Numero 4: 2
Numero 5: 0
Numero 6: 0
Numero 7: 5
Numero 8: 5
Numero 9: 0
Espacios: 13
Tabulado: 1
Saltos linea: 4
Otros: 43
Debo de admitir que en esta parte me adelanté con el tema del uso del switch y lo que hice fue muy distinto a lo que venía en el libro.
Funciones
Ya me había adelantado con el tema de las funciones pero aún así no me quiero saltar este tema. El manual nos muestra una función que eleva un número por un número exponente. Voy a hacer la implementación desde 0.
openjazz$ vim func-expo.c
#include <stdio.h>
int potencia(int numa, int numb)
{
int result = 0;
if(numa==0 && numb==0)
{
return -1;
}
if(numb==0)
{
return 1;
}
if(numa==0)
{
return 0;
}
result = numa;
for(int i=1; i<numb; i++)
{
result = result * numa;
}
return result;
}
int main(void)
{
int numa = 0;
int numb = 0;
int result = 0;
printf("Ingrese el numero A:\n");
scanf("%d", &numa);
printf("Ingrese el numero B:\n");
scanf("%d", &numb);
result = potencia(numa, numb);
printf("Resultado: %d\n", result);
return 0;
}
openjazz$ clang func-expo.c
openjazz$ ./a.out
Ingrese el numero A:
2
Ingrese el numero B:
6
Resultado: 64
En esta agregué algunos if un poco sucios por el tema de las definiciones matemáticas.
Algo que no probé es el tema de los números negativos, pero creo que así es más que suficiente.
Arguments - Call by Value
La siguiente sección del libro es bastante corta. En síntesis, nos explica que los argumentos que se pasan a una funcion se crean localmente dentro de la función al contrario de Fortran, en donde los parámetros que se pasan a una función son un apunte directo a la variable original de entrada, por lo que dentro de la función se pueden alterar valores de manera global. Es un concepto bastante fácil de comprender porque este también es el comportamiento de Java. De hecho, sinceramente, no sabía ni me había planteado que otros lenguajes lo hicieran de otra manera.
Char Arrays
Al parecer la manera en la que C implementa los strings es muy distinta a Java. En Java
los strings son objetos, pero como en C no hay objetos como tal como en C++, lo más
parecido son los arrays de variables tipo char. En esta sección del libro se hacen varias pŕactica.
En mi caso, voy a hacer una implementación desde 0 en donde reciva un input, guardarlo en un array de
char e imprimir solamente las palabras con más de 5 caracteres.
openjazz$ vim array-char.c
#include <stdio.h>
void printString(char string[2048], int limit)
{
for(int i = 0; i < limit; i++)
{
printf("%c", string[i]);
if(string[i] == '\n')
{
printf("\n");
}
}
return;
}
int main(void)
{
char string[2048];
char c = '0';
int i = 0;
while((c = getchar()) != EOF)
{
string[i] = c;
i++;
}
printf("Full input:\n----------------\n");
printString(string, i);
printf("\n----------------\nSolo palabras de + de 5 caracteres:\n");
int size = i;
i = 0;
int length = 0;
char word[2048];
for(int l = 0; l <= size; l++)
{
if(string[l] != ' ')
{
word[length] = string[l];
length++;
} else if (string[l] == ' ')
{
if(length > 5)
{
printString(word, length);
printf("\n");
}
length = 0;
}
}
return 0;
}
openjazz$ clang array-char.c
openjazz$ ./a.out
hola esta es una prueba
de mi programa que
maneja cadenas de texto
Full input:
----------------
hola esta es una prueba
de mi programa que
maneja cadenas de texto
----------------
Solo palabras de + de 5 caracteres:
prueba
programa
maneja
cadenas
La verdad este programa es de todo menos perfecto pero lo considero un ejercicio útil.