Steven-Jaro
☰ Menú

Capítulo 1 Tutorial de introduccion a C

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.