1. Programación procedimental
1.1. Entrada y salida
1.1.1. Número de la apuesta
El siguiente programa pregunta una cantidad de dinero a apostar, luego imprime con formato los números en la salida estándar de forma amigable.
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
int main(void)
{
const int lucky_number = 46;
const short day = 15;
double money = 0.0;
scanf("%lg", &money);
printf("Apueste %.2lf colones al %d este domingo %hd\n", money, lucky_number, day);
return 0;
}
Si copia el código anterior en un archivo bet.c
puede compilarlo y ejecutar el programa con los comandos siguientes. Los textos precedidos por $
son comandos escritos por el usuario, y #
son comentarios:
# Compilar y generar ejecutable llamado bet
$ cc -g -Wall -Wextra bet.c -o bet
# Correr el programa llamado bet
$ ./bet
1.1.2. Formatear la lista de clase
eMatrícula genera listas de clase con exceso de espacio en blanco. El siguiente es un ejemplo con datos ficticios. Supóngase que el archivo se llama lista.txt
.
1
2
3
4
5
6
7
8
9
B62423 \t 8 \t RIOS JUAREZ BLANCA ROSA \t
\t\t
\t\t
B82342 \t 8.19 \t RODRIGUEZ GOMEZ IGNACIO \t
\t\t
\t\t
B83472 \t 9.71 \t BRENES SOTO ANA IRIS \t
\t\t
\t\t
El siguiente programa lee una lista de clase generada por eMatricula y la imprime con formato. El programa ignora el espacio en blanco redundante generado por eMatricula.
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
int main(void)
{
char id[6 + 1];
char name[65];
double ppm = 0.0;
while ( scanf("%6s %lf %64[^\n]", id, &ppm, name) == 3 )
printf("%6s %5.2lf %64s\n", id, ppm, name);
return 0;
}
Si copia el código anterior en un archivo format.c
, este comando lo compila en un ejecutable:
cc -g -Wall -Wextra format.c -o format
Al invocar el programa con la lista.txt
como entrada estándar, produciría una salida:
$ ./format < lista.txt
B62423 8.00 RIOS JUAREZ BLANCA ROSA
B82342 8.19 RODRIGUEZ GOMEZ IGNACIO
B83472 9.71 BRENES SOTO ANA IRIS
1.1.3. Archivos nombrados
La siguiente variación del programa que formatea la lista de clase puede recibir un nombre de archivo por argumento de línea de comandos. Si se omite, supone que los datos se proveerán en la en la entrada estándar.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
char id[6 + 1];
char name[65];
double ppm = 0.0;
bool is_named_file = false;
//FILE* input = fopen("input001.txt", "r");
FILE* input = stdin;
if ( argc >= 2 )
{
input = fopen(argv[1], "r");
if ( ! input )
{
fprintf(stderr, "format2: error: could not open %s\n", argv[1]);
return 1; // EXIT_FAILURE
}
is_named_file = true;
}
while ( fscanf(input, "%6s %lf %64[^\n]", id, &ppm, name) == 3 )
fprintf(stdout, "%6s %5.2lf %64s\n", id, ppm, name);
//if ( argc >= 2 )
if ( is_named_file )
fclose(input);
return EXIT_SUCCESS;
}
El programa se puede invocar de las siguientes dos formas
# Lee los datos del archivo lista.txt
$ ./format2 lista.txt
B62423 8.00 RIOS JUAREZ BLANCA ROSA
B82342 8.19 RODRIGUEZ GOMEZ IGNACIO
B83472 9.71 BRENES SOTO ANA IRIS
# Lee los datos de la entrada estándar
$ ./format2 < lista.txt
B62423 8.00 RIOS JUAREZ BLANCA ROSA
B82342 8.19 RODRIGUEZ GOMEZ IGNACIO
B83472 9.71 BRENES SOTO ANA IRIS
# Si se da un nombre de archivo que no existe
$ ./format2 jiji.txt
format2: error: could not open jiji.txt
1.2. Expresiones y condicionales
Con esta sección se inicia el proceso de resolución de problemas, que consta de:
Análisis. Comprender el problema. Se recomienda rotular cada trozo de la entrada y salida con los nombres con que el cliente los llama. Como prueba, el estudiante debe estar seguro de que comprende qué debe hacer el programa y saber qué es cada trozo de información de la entrada y de la salida.
Diseño. Resolver el problema. Se recomienda al estudiante resolver el problema, traduciendo de la entrada a la salida, sin pensar en computadoras. Debe usar sus habilidades humanas y matemáticas. No se puede instruir una computadora si no se sabe resolver el problema como humano. Luego de resolver el problema como humano, escribir un diseño siguiendo las convenciones del paradigma de programación usado. Para el paradigma de programación procedimental consiste en escribir un algoritmo usando los ocho tipos de instrucciones:
-
Leer un valor(es)
-
Imprimir un valor(es)
-
Crear una variable con un valor
-
Cambiarle el valor a una variable
-
Condicionar una instrucción
-
Repetir una instrucción
-
Hacer una subtarea
-
Definir una subtarea
La fase de diseño es la más difícil del proceso, y la que más caracteriza al trabajo ingenieril. Una vez elaborado el algoritmo, se debe probar. El estudiante puede ejecutar el algoritmo paso a paso al pie de la letra, anotando en una tabla los valores de las variables, marcando con un cursor la entrada, y escribiendo los resultados en un texto de salida. Cuando el algoritmo resuelva el problema se pasa a la siguiente fase.
Implementación. Consiste en traducir instrucción por instrucción a un lenguaje de programación que se apegue al paradigma. Requiere mucho dominio del lenguaje de programación, y se considera la fase más fácil del proceso, a veces conocida como "codificación". Cuando el programa está implementado, se prueba contra usando algún mecanismo de pruebas de software (testing). En este curso se usa pruebas de caja negra apoyados por un juez automático en línea. El siguiente ejemplo recorre todas las fases del proceso descrito.
1.2.1. Eliminar la nota más baja
Algoritmo parcial del Grupo 04
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
main:
Read group count
Repeat group_index for group count
//for ( int group_index = 0; group_index < group_count; ++group_index )
Read group number
Read student_count
Repeat student_index for student_count
Read student_id
Create lab_grades as an array of 10 real numbers
Read lab_grades
...
Read professor_grade
Create lab_lowest_grade as the result of calculating the lowest grade
Create quiz_lowest_grade as the result of calculating the lowest grade
Create lab_sum as the sum of all labs
Create right_lab_grade as (lab_sum - lab_lowes_grade)/9
...
Calculate the lowest grade:
Create min as positive infinite
For each grade in grades
If grade is lower than min then
Change min as grade
Return min
1.3. Indirección, arreglos y matrices
1.3.1. Imprimir el rango (punteros)
Programa que lee los extremos de un rango de los argumentos en la línea de comandos o de la entrada estándar e imprime los números en el rango. Si el rango está invertido, intercambia los dos extremos del rango. Utiliza indirección (punteros) para realizar el intercambio.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <stdio.h>
// Subroutine declaration or Function prototype
void swap(long long* value1, long long* value2);
// main:
int main(void)
{
// Read min, max
long long min = 0, max = 0;
if ( scanf("%lld %lld", &min, &max) == 2 )
{
// If min > max then
if ( min > max )
{
// Swap min with max
//. 1000. 1008 min==5, &min==1000
swap( &min, &max ); // Arguments
}
// Repeat index from min to max inclusive do
for ( long long index = min; index <= max; ++index )
{
// Print index
printf("%lld%c", index, (index == max ? '\n' : ' ') );
}
}
else
return 1;
return 0;
}
// Swap <value1> with <value2>:
//. 1000 1008
void swap(long long* value1, long long* value2) // Params: DataType varName = initValue
{
// Create a copy of value1
// = *1000.
// value1=1000 &value1==1016 *value1==5
const long long value1_copy = *value1; // desreferencing
// Assign value1 as value2
// 1000 := 1008
*value1 = *value2;
// Assign value2 as the copy of value1
// 1008 := 1000
*value2 = value1_copy;
}
1.3.2. Leer e imprimir el arreglo
Lee un arreglo de números flotantes de doble precisión y lo imprime en la salida estándar.
Versión 1: Crea el arreglo en memoria de pila. Produce una vulnerabilidad de desbordamiento de pila (stack overflow) y es un código que no debería usarse en producción.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
/**
@brief Read @a value_count doubles from stdin and store them in values array
@param value_count Number of elements to be read from stdin
@param values Array of elements. It must not be NULL
@return An error code, 0 for success
*/
int read_values(const size_t value_count, double values[value_count]);
/**
...
*/
void print_values_index(const size_t value_count, const double values[value_count]);
int main(void)
{
size_t value_count = 0;
if ( scanf("%zu", &value_count) == 1 )
{
double values[value_count];
if ( read_values(value_count, values) == EXIT_SUCCESS )
print_values_index(value_count, values);
}
else
return 1;
return 0;
}
int read_values(const size_t value_count, double values[value_count])
{
for ( size_t index = 0; index < value_count; ++index )
if ( scanf("%lf", &values[index]) != 1 )
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
void print_values_index(const size_t value_count, const double values[value_count])
{
for ( size_t index = 0; index < value_count; ++index )
printf("%zu: %lf\n", index, values[index]);
}
Versión 2: Crea el arreglo en memoria dinámica, pero introduce una fuga de memoria. Tampoco debe usarse en producción.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
/**
@brief Read @a value_count doubles from stdin and store them in values array
@param value_count Number of elements to be read from stdin
@param values Array of elements. It must not be NULL
@return An error code, 0 for success
*/
int read_values(const size_t value_count, double values[value_count]);
/**
...
*/
void print_values_index(const size_t value_count, const double values[value_count]);
int main(void)
{
size_t value_count = 0;
if ( scanf("%zu", &value_count) == 1 && errno == 0 )
{
double* values = (double*) malloc( value_count * sizeof(double) );
if ( values == NULL )
return (void) fprintf(stderr, "error: not enough memory\n"), 2;
if ( read_values(value_count, values) == EXIT_SUCCESS )
print_values_index(value_count, values);
}
else
return 1;
return 0;
}
int read_values(const size_t value_count, double values[value_count])
{
for ( size_t index = 0; index < value_count; ++index )
// &values[index] == &2000[0]
if ( scanf("%lf", &values[index]) != 1 )
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
void print_values_index(const size_t value_count, const double values[value_count])
{
for ( size_t index = 0; index < value_count; ++index )
if ( index == value_count - 1 )
printf("%zu: %lf\n", index, values[index]);
}
Versión 3: Crea el arreglo en memoria dinámica y lo libera. Puede usarse en producción.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
/**
@brief Read @a value_count doubles from stdin and store them in values array
@param value_count Number of elements to be read from stdin
@param values Array of elements. It must not be NULL
@return An error code, 0 for success
*/
int read_values(const size_t value_count, double values[]);
/**
...
*/
void print_values_index(const size_t value_count, const double* values);
void print_values_pointer(const size_t value_count, const double* values);
int main(void)
{
size_t value_count = 0;
if ( scanf("%zu", &value_count) == 1 && errno == 0 )
{
double* values = (double*) calloc( value_count, sizeof(double) );
if ( values == NULL )
return (void) fprintf(stderr, "error: not enough memory\n"), 2;
if ( read_values(value_count, values) == EXIT_SUCCESS )
print_values_pointer(value_count, values);
free(values);
}
else
return 1;
return 0;
}
int read_values(const size_t value_count, double values[])
{
for ( size_t index = 0; index < value_count; ++index )
// &values[index] == &2000[0]
if ( scanf("%lf", &values[index]) != 1 )
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
void print_values_index(const size_t value_count, const double* values)
{
for ( size_t index = 0; index < value_count; ++index )
if ( index == value_count - 1 )
printf("%zu: %lf\n", index, values[index]);
}
void print_values_pointer(const size_t value_count, const double* values)
{
for ( size_t index = 0; index < value_count; ++index )
if ( index == 0 || index == value_count - 1 )
// *(2000 + 2 * sizeof(const double))
// *(2000 + 2 * 8)
// *(2000 + 16)
// *(2016)
printf("%zu: %lf\n", index, *(values + index) );
// pointer[index] == *(pointer + index)
}
1.3.3. Imprimir la matriz
Lee una matriz de números flotantes de doble precisión y la imprime en la salida estándar. Muestra el trabajo básico de matrices: creación en memoria dinámica, recorrido, y eliminación. Provee funciones para crear y destruir matrices de cualquier tipo en C.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
/**
@brief Read @a value_count doubles from stdin and store them in values array
@param value_count Number of elements to be read from stdin
@param values Array of elements. It must not be NULL
@return An error code, 0 for success
*/
int read_values(const size_t value_count, double values[]);
/**
...
*/
void print_values_index(const size_t value_count, const double* values);
void print_values_pointer(const size_t value_count, const double* values);
void** create_matrix(size_t row_count, size_t col_count, size_t element_size);
void free_matrix(const size_t row_count, void** matrix);
void free_matrix_double(const size_t row_count, double** matrix);
double** create_matrix_double(size_t row_count, size_t col_count);
void free_matrix_double(const size_t row_count, double** matrix);
int read_matrix_double(const size_t row_count, const size_t col_count, double matrix[row_count][col_count]);
int read_matrix_double_ptr(const size_t row_count, const size_t col_count, double** matrix);
void print_matrix_double(const size_t row_count, const size_t col_count, double** matrix);
// Used as a draft
int main_double(void)
{
size_t row_count = 0, col_count = 0;
if ( scanf("%zu %zu", &row_count, &col_count) == 2 && errno == 0 )
{
double** values = create_matrix_double(row_count, col_count);
/*
double values[row_count][col_count];
double** values_ptr = (double**)values;
row_count = 3;
double array[row_count]; // 1016
sizeof(array) == 24
// 1016
double* array_ptr = array;
sizeof(array_ptr) == 8
array_ptr = array_ptr + 2; // 1016 + 2*sizeof(double) == 1016 + 16 == 1032
*array_ptr = 3.14;
array = 1032;
const double* const array_ptr2 = array;
array_ptr2 = array_ptr2 + 2;
*/
if ( values == NULL )
return (void) fprintf(stderr, "error: not enough memory\n"), 2;
if ( read_matrix_double_ptr(row_count, col_count, values) == EXIT_SUCCESS )
print_matrix_double(row_count, col_count, values);
free_matrix_double(row_count, values);
}
else
return 1;
return 0;
}
int main(void)
{
size_t row_count = 0, col_count = 0;
if ( scanf("%zu %zu", &row_count, &col_count) == 2 && errno == 0 )
{
double** values = (double**) create_matrix(row_count, col_count, sizeof(double));
if ( values == NULL )
return (void) fprintf(stderr, "error: not enough memory\n"), 2;
if ( read_matrix_double_ptr(row_count, col_count, values) == EXIT_SUCCESS )
print_matrix_double(row_count, col_count, values);
free_matrix(row_count, (void**)values);
}
else
return 1;
return 0;
}
void** create_matrix(size_t row_count, size_t col_count, size_t element_size)
{
void** matrix = (void**) calloc( row_count, sizeof(void*) );
if ( matrix == NULL )
return NULL;
for ( size_t row = 0; row < row_count; ++row )
if ( ( matrix[row] = calloc( col_count, element_size ) ) == NULL )
return free_matrix(row_count, matrix), NULL;
return matrix;
}
void free_matrix(const size_t row_count, void** matrix)
{
if ( matrix )
for ( size_t row = 0; row < row_count; ++row )
free( matrix[row] );
free(matrix);
}
double** create_matrix_double(size_t row_count, size_t col_count)
{
double** matrix = (double**) calloc( row_count, sizeof(double*) );
for ( size_t row = 0; row < row_count; ++row )
matrix[row] = (double*) calloc( col_count, sizeof(double) );
return matrix;
}
void free_matrix_double(const size_t row_count, double** matrix)
{
for ( size_t row = 0; row < row_count; ++row )
free( matrix[row] );
free(matrix);
}
int read_matrix_double(const size_t row_count, const size_t col_count, double matrix[row_count][col_count])
{
for ( size_t row = 0; row < row_count; ++row )
for ( size_t col = 0; col < col_count; ++col )
if ( scanf("%lf", &matrix[row][col]) != 1 )
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
int read_matrix_double_ptr(const size_t row_count, const size_t col_count, double** matrix)
{
for ( size_t row = 0; row < row_count; ++row )
for ( size_t col = 0; col < col_count; ++col )
if ( scanf("%lf", &matrix[row][col]) != 1 )
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
void print_matrix_double(const size_t row_count, const size_t col_count, double** matrix)
{
for ( size_t row = 0; row < row_count; ++row )
for ( size_t col = 0; col < col_count; ++col )
printf("%6.2lf\n", *(*(matrix + row) + col));
}
1.3.4. Tipos de arreglos en C
Las matrices no existen en la memoria de la máquina, se construyen con arreglos de elementos y arreglos de indirección. El siguiente código muestra cómo crear diferentes tipos de arreglos en C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void arrays();
void matrixes();
void multidimensional_arrays();
int main()
{
srand( time(NULL) );
arrays();
matrixes();
multidimensional_arrays();
return 0;
}
void print_array(long double array[], size_t size)
{
printf("\t{");
for ( size_t index = 0; index < size; ++index )
printf("%s %Lg", index ? "," : "", array[index] );
printf(" }\n");
}
void arrays()
{
// 1. Literal array (continuous)
long double literal_array[] = {-1.0L, -0.5L, 0.0L, 0.5L, 1.0L};
size_t literal_size = sizeof(literal_array) / sizeof(literal_array[0]);
printf("literal_array:\n");
print_array(literal_array, literal_size);
printf("\tcount: %zu elements\n", literal_size);
printf("\tsizeof: %zu bytes\n", sizeof(literal_array));
// 2. Automatic array (continuous)
size_t automatic_size = 3 + rand() % 8;
long double automatic_array[automatic_size];
for ( size_t index = 0; index < automatic_size; ++index )
automatic_array[index] = index;
printf("\nautomatic_array:\n");
print_array(automatic_array, automatic_size);
printf("\tcount: %zu elements\n", automatic_size);
printf("\tsizeof: %zu bytes\n", sizeof(automatic_array));
// 3. Dynamic allocated array (not Dynamic array)
size_t dynamic_alloc_size = 3 + rand() % 8;
long double* dynamic_alloc_array = (long double*)malloc(dynamic_alloc_size * sizeof(long double));
for ( size_t index = 0; index < dynamic_alloc_size; ++index )
dynamic_alloc_array[index] = index;
printf("\ndynamic_alloc_array:\n");
print_array(dynamic_alloc_array, dynamic_alloc_size);
printf("\tcount: %zu elements\n", dynamic_alloc_size);
printf("\tsizeof: %zu bytes (long double*)\n", sizeof(dynamic_alloc_array));
free(dynamic_alloc_array);
}
void print_literal_matrix(size_t rows, size_t cols, long double matrix[rows][cols])
{
for ( size_t row_index = 0; row_index < rows; ++row_index )
{
putchar('\t');
for ( size_t col_index = 0; col_index < cols; ++col_index )
printf("%5Lg ", matrix[row_index][col_index] );
putchar('\n');
}
}
void print_continous_matrix(long double* matrix, size_t rows, size_t cols)
{
for ( size_t row_index = 0; row_index < rows; ++row_index )
{
putchar('\t');
for ( size_t col_index = 0; col_index < cols; ++col_index )
printf("%5Lg ", matrix[row_index * cols + col_index] );
putchar('\n');
}
}
void print_uncontinous_matrix(long double** matrix, size_t rows, size_t cols)
{
for ( size_t row_index = 0; row_index < rows; ++row_index )
{
printf("\t{");
for ( size_t col_index = 0; col_index < cols; ++col_index )
printf("%5Lg ", matrix[row_index][col_index] );
printf(" }\n");
}
}
void literal_matrix()
{
long double matrix[][3] =
{
{1.1L, 1.2L, 1.3L},
{2.1L, 2.2L, 2.3L},
};
size_t cols = 3;
size_t rows = sizeof(matrix) / cols / sizeof(matrix[0][0]);
printf("\nliteral_matrix:\n");
print_literal_matrix(rows, cols, matrix);
printf("\tcount: %zu rows x %zu cols\n", rows, cols);
printf("\tsizeof: %zu bytes\n", sizeof(matrix));
}
void automatic_matrix()
{
// 2. Automatic matrix (continuous)
size_t rows = 3 + rand() % 8;
size_t cols = 3 + rand() % 8;
long double matrix[rows][cols];
for ( size_t row_index = 0; row_index < rows; ++row_index )
for ( size_t col_index = 0; col_index < cols; ++col_index )
matrix[row_index][col_index] = (row_index + 1) + (col_index + 1) / 10.0;
printf("\nautomatic_matrix:\n");
print_literal_matrix(rows, cols, matrix);
printf("\tcount: %zu rows x %zu cols\n", rows, cols);
printf("\tsizeof: %zu bytes\n", sizeof(matrix));
}
void continous_dynamic_allocated_matrix()
{
// 3. Dynamic allocated matrix (continous) (not Dynamic matrix)
size_t rows = 3 + rand() % 8;
size_t cols = 3 + rand() % 8;
long double* matrix = (long double*)malloc(rows * cols * sizeof(long double));
for ( size_t row_index = 0; row_index < rows; ++row_index )
for ( size_t col_index = 0; col_index < cols; ++col_index )
matrix[row_index * cols + col_index] = (row_index + 1) + (col_index + 1) / 10.0;
printf("\ndynamic_alloc_matrix:\n");
print_continous_matrix(matrix, rows, cols);
printf("\tcount: %zu rows x %zu cols\n", rows, cols);
printf("\tsizeof: %zu bytes (long double*)\n", sizeof(matrix));
free(matrix);
}
void uncontinous_dynamic_allocated_matrix()
{
// 4. Dynamic allocated matrix (uncontinous) (not Dynamic matrix)
size_t rows = 3 + rand() % 8;
size_t cols = 3 + rand() % 8;
// Allocate a vector of vectors
long double** matrix = (long double**)malloc(rows * sizeof(long double*));
for ( size_t row_index = 0; row_index < rows; ++row_index )
matrix[row_index] = (long double*)malloc(cols * sizeof(long double));
// Initialize values
for ( size_t row_index = 0; row_index < rows; ++row_index )
for ( size_t col_index = 0; col_index < cols; ++col_index )
matrix[row_index][col_index] = (row_index + 1) + (col_index + 1) / 10.0;
printf("\nuncontinous_dynamic_alloc_matrix:\n");
print_uncontinous_matrix(matrix, rows, cols);
printf("\tcount: %zu rows x %zu cols\n", rows, cols);
printf("\tsizeof: %zu bytes (long double**)\n", sizeof(matrix));
// Free each row first, then the vector of vectors
for ( size_t row_index = 0; row_index < rows; ++row_index )
free(matrix[row_index]);
free(matrix);
}
void matrixes()
{
literal_matrix();
automatic_matrix();
continous_dynamic_allocated_matrix();
uncontinous_dynamic_allocated_matrix();
}
void multidimensional_arrays()
{
// 3. multidimensional array
long double multidimensional_array[2][3][4] =
{
{
{11.1L, 11.2L, 11.3L, 11.4L},
{12.1L, 12.2L, 12.3L, 12.4L},
{13.1L, 13.2L, 13.3L, 13.4L},
},
{
{21.1L, 21.2L, 21.3L, 21.4L},
{22.1L, 22.2L, 22.3L, 22.4L},
{23.1L, 23.2L, 23.3L, 23.4L},
},
};
printf("\nmultidimensional_array:\n");
printf("\tsizeof: %zu\n", sizeof(multidimensional_array));
// To be done...
}
1.4. Cadenas de caracteres
1.4.1. Imprimir argumentos en línea de comandos
Imprime todos los argumentos dados por el usuario al invocar el programa en la línea de comandos.
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
//int main(int argument_count, char* argument_vector[])
int main(int argc, char* argv[])
{
for ( int arg_index = 0; arg_index < argc; ++arg_index )
printf("%d [%s]\n", arg_index, argv[arg_index]);
return 0;
}
1.4.2. Encontrar la extensión
Programa que recibe nombres de archivo en los argumentos de línea de comandos y para cada uno de ellos encuentra la extensión y la imprime. El algoritmo para buscar la extensión de archivo se implementa en dos variantes: con indexación y con arimética de punteros.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <stdio.h>
/**
@brief Convert a text file containing floating point numbers to its binary form
@param input_filename The path and name of the file to be converted
@return An error code, 0 for success
*/
int process_file(const char* input_filename);
int find_extension_index(const char* filename);
const char* find_extension_ptr(const char* filename);
//int main(int argument_count, char* argument_vector[])
int main(int argc, char* argv[])
{
for ( int arg_index = 1; arg_index < argc; ++arg_index )
process_file( argv[arg_index] );
return 0;
}
int process_file(const char* input_filename)
{
int extension_index = find_extension_index(input_filename);
printf("extension idx: [%s]\n", &input_filename[extension_index]);
printf("extension idx: [%s]\n", input_filename + extension_index);
const char* extension = find_extension_ptr(input_filename);
printf("extension ptr: [%s]\n", extension);
return 0; // Success
}
//size_t find_extension_index(size_t count, double* values);
int find_extension_index(const char* filename)
{
int last_dot = -1, index = 0;
while ( filename[index] )
{
if ( filename[index] == '.' )
last_dot = index;
++index;
}
return last_dot == -1 ? -1 : last_dot + 1;
}
const char* find_extension_ptr(const char* filename)
{
const char* last_dot = NULL;
while ( *filename )
{
if ( *filename == '.' )
last_dot = filename;
++filename; // filename++; filename += 1; filename = filename + 1;
}
return last_dot ? last_dot + 1 : last_dot;
}
2. Programación orientada a objetos
2.1. Clases y objetos
2.1.1. Espacios de nombres
Un programa en C es un programa en C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
/*
#include <thirdparty.h>
typedef double main;
int main;
*/
int main(void)
{
printf("Hello world C\n");
return 0;
}
1
2
3
4
5
6
7
#include <stdio.h>
int main(void)
{
printf("Hello world C\n");
return 0;
}
Por tanto se puede compilar con C++ un programa en C, mientras tenga extensión .cpp
:
gcc -g -Wall -Wextra -std=c18 main.c -o main_c
g++ -g -Wall -Wextra -std=c++17 main.cpp -o main_cpp
2.1.2. Representación procedimental de los objetos
La máquina computacional no es orientada a objetos. El paradigma de programación de alto nivel más cercano a la máquina es el procedimental. Por tanto, el concepto de objeto es sólo comprendido por el compilador, quien debe traducirlo a constructos más primitivos. Comprender cómo los objetos pueden ser representados con constructos procedimientales es de utilidad para el/la informático/a para ver la computadora como una caja de cristal y no una caja mágica. El siguiente código muestra una potencial representación de los objetos del ejemplo de fracciones usando registros y funciones libres (con punteros hacia los registros). Para cada ejemplo se muestra una pareja de archivos, el original en C++ y su correspondiente adptación a C.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <limits>
#include "Fraction.hpp"
int main()
{
Fraction fr1;
fr1.read();
printf("Your fraction: ");
fr1.print();
putchar('\n');
Fraction fr2;
printf("Fraction2: ");
fr2.print();
putchar('\n');
Fraction fr3(3, 15);
printf("Fraction3: ");
fr3.print();
putchar('\n');
Fraction fr4{-4};
printf("Fraction4: ");
fr4.print();
putchar('\n');
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>
#include "Fraction.h"
int main()
{
Fraction fr1;
Fraction_ctor(&fr1);
Fraction_read(&fr1);
printf("Your fraction: ");
Fraction_print(&fr1);
putchar('\n');
Fraction fr2;
Fraction_ctor(&fr2);
printf("Fraction2: ");
Fraction_print(&fr2);
putchar('\n');
Fraction fr3;
Fraction_ctor_ll_ll(&fr3, 3, 5);
printf("Fraction3: ");
Fraction_print(&fr3);
putchar('\n');
// fr3.numerator = 3;
// fr3.setDenominator(5);
Fraction fr4;
Fraction_ctor_ll(&fr4, -4);
printf("Fraction4: ");
Fraction_print(&fr4);
putchar('\n');
}
La conversión procedimental separa la estructura del comportamiento. La estructura (atributos) son representados con un registro (struct
). El comportamiento es representado con funciones libres. Las declaraciones de funciones de la clase se convierten en declaraciones en el archivo de encabezado (.h
), y a todos los métodos de instancia (no estáticos) se les agrega automáticamente un puntero en el primer parámetro hacia el registro donde debe actuar. A este puntero se le llama el parámetro oculto `this`, y es el que enlaza las funciones libres con su registro.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#ifndef FRACTION_H
#define FRACTION_H
// \frac{a}{b}: a, b \epsilon \mathbb{Z}, b\neq 0
typedef long long FractionPart;
//struct Fraction // PDO=Pure Data Object
class Fraction
{
private:
FractionPart numerator = 0;
FractionPart denominator;
public:
Fraction(FractionPart numerator = 0, FractionPart denominator = 1)
: numerator(numerator)
, denominator{denominator ? denominator : 1}
{
/*
int x;
x = 1;
*/
// These are assignments not initialization
// this->numerator = 0;
// this->denominator = 1;
this->simplify();
}
void read();
void print()
{
std::cout << /*this->*/numerator << '/' << this->denominator;
}
void simplify();
FractionPart greatestCommonDivisor()
{
return greatestCommonDivisor(this->numerator, this->denominator);
}
static FractionPart greatestCommonDivisor(FractionPart a, FractionPart b);
};
#endif // FRACTION_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#ifndef FRACTION_H
#define FRACTION_H
// \frac{a}{b}: a, b \epsilon \mathbb{Z}, b\neq 0
typedef long long FractionPart;
//struct Fraction // PDO=Pure Data Object
typedef struct
{
FractionPart numerator;
FractionPart denominator;
} Fraction;
void Fraction_ctor_ll_ll(Fraction* this, FractionPart numerator, FractionPart denominator);
void Fraction_ctor_ll(Fraction* this, FractionPart numerator);
void Fraction_ctor(Fraction* this);
void Fraction_read(Fraction* this);
void Fraction_print(Fraction* this);
void Fraction_simplify(Fraction* this);
FractionPart Fraction_greatestCommonDivisor(Fraction* this);
/*static*/ FractionPart Fraction_greatestCommonDivisor_ll_ll(FractionPart a, FractionPart b);
/*
extern "C"
{
FractionPart min(FractionPart a, FractionPart b)
{
return a < b ? a : b;
}
} // extern "C"
*/
#endif // FRACTION_H
Dado que en C no hay espacios de nombres ni sobrecarga de subrutinas, el compilador de C++ debe cambiar los nombres que el programador escogió por identificadores únicos. Los nombres escogidos no son estándar, sino que cada compilador escoge su propia nomenclatura, lo que hace al código objeto incompatible entre compiladores. A este proceso de cambio automático de nombres se le llama name mangling. En el siguiente ejemplo se usa la convención Clase_metodo()
para los nombres de los métodos. Note cómo el compilador debe usar el puntero oculto this
para poder acceder a los atributos de la forma this→attribute
, en caso de que el programador no lo haya hecho:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include "Fraction.h"
void Fraction::read()
{
std::cin >> this->numerator;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '/');
std::cin >> this->denominator;
this->simplify();
}
void Fraction::simplify()
{
if ( this->denominator < 0 )
{
this->numerator *= -1;
this->denominator *= -1;
}
// x /= exp
// x = x / (exp)
FractionPart greatestCommonDivisor = Fraction::greatestCommonDivisor(this->numerator, this->denominator);
this->numerator /= greatestCommonDivisor;
this->denominator /= greatestCommonDivisor;
}
FractionPart Fraction::greatestCommonDivisor(FractionPart a, FractionPart b)
{
if ( a <= 0 )
a = -a;
if ( b <= 0 )
b = -b;
// Source: https://en.wikipedia.org/wiki/Euclidean_algorithm
while ( b )
{
FractionPart t = b;
b = a % b;
a = t;
}
return a;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <stdio.h>
#include "Fraction.h"
void Fraction_ctor_ll_ll(Fraction* this, FractionPart numerator, FractionPart denominator)
{
this->numerator = numerator;
this->denominator = denominator ? denominator : 1;
Fraction_simplify(this);
}
void Fraction_ctor_ll(Fraction* this, FractionPart numerator)
{
this->numerator = numerator;
this->denominator = 1;
Fraction_simplify(this);
}
void Fraction_ctor(Fraction* this)
{
this->numerator = 0;
this->denominator = 1;
Fraction_simplify(this);
}
// void Fraction::read()
void Fraction_read(Fraction* this)
{
scanf("%lld/%lld", &this->numerator, &this->denominator);
// std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '/');
Fraction_simplify(this);
}
void Fraction_print(Fraction* this)
{
printf("%lld", this->numerator);
printf("%c", '/');
printf("%lld", this->denominator);
}
// void Fraction::simplify()
void Fraction_simplify(Fraction* this)
{
if ( this->denominator < 0 )
{
this->numerator *= -1;
this->denominator *= -1;
}
// x /= exp
// x = x / (exp)
//FractionPart greatestCommonDivisor = Fraction_greatestCommonDivisor(this);
FractionPart greatestCommonDivisor = Fraction_greatestCommonDivisor_ll_ll(this->numerator, this->denominator);
this->numerator /= greatestCommonDivisor;
this->denominator /= greatestCommonDivisor;
}
FractionPart Fraction_greatestCommonDivisor(Fraction* this)
{
return Fraction_greatestCommonDivisor_ll_ll(this->numerator, this->denominator);
}
/*static*/ FractionPart Fraction_greatestCommonDivisor_ll_ll(FractionPart a, FractionPart b)
{
if ( a <= 0 )
a = -a;
if ( b <= 0 )
b = -b;
// Source: https://en.wikipedia.org/wiki/Euclidean_algorithm
while ( b )
{
FractionPart t = b;
b = a % b;
a = t;
}
return a;
}
2.2. Sobrecarga de operadores
2.2.1. Calculadora fraccional
Muestra cómo sobrecargar operadores para realizar operaciones con fracciones, como sumas, restas. Introduce el concepto de referencia.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include <limits>
#include "Fraction.h"
int main()
{
Fraction fr1;
fr1.read();
std::cout << "Your fraction: ";
fr1.print();
std::cout << std::endl;
Fraction fr2;
std::cout << "Fraction2: ";
fr2.print();
std::cout << std::endl;
Fraction fr3(3, 15);
std::cout << "Fraction3: ";
fr3.print();
std::cout << std::endl;
// fr3.numerator = 3;
// fr3.setDenominator(5);
Fraction fr4{-4};
std::cout << "Fraction4: ";
fr4.print();
std::cout << std::endl;
Fraction sum = fr1 + fr3;
// Fraction sum = fr1.operator+(fr3);
// Fraction sum = fr1.add(fr3);
std::cout << "Sum: ";
sum.print();
std::cout << std::endl;
// std::cin >> fr1;
// std::cout << fr1 << std::endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#ifndef FRACTION_H
#define FRACTION_H
// \frac{a}{b}: a, b \epsilon \mathbb{Z}, b\neq 0
typedef long long FractionPart;
//struct Fraction // PDO=Pure Data Object
class Fraction
{
private:
FractionPart numerator = 0;
FractionPart denominator;
public:
Fraction(FractionPart numerator = 0, FractionPart denominator = 1)
: numerator(numerator)
, denominator{denominator ? denominator : 1}
{
/*
int x;
x = 1;
*/
// These are assignments not initialization
// this->numerator = 0;
// this->denominator = 1;
this->simplify();
}
void read();
void print()
{
std::cout << /*this->*/numerator << '/' << this->denominator;
}
void simplify();
FractionPart greatestCommonDivisor()
{
return greatestCommonDivisor(this->numerator, this->denominator);
}
static FractionPart greatestCommonDivisor(FractionPart a, FractionPart b);
Fraction add(Fraction other);
Fraction operator+(Fraction other);
};
#endif // FRACTION_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <iostream>
#include "Fraction.h"
void Fraction::read()
{
std::cin >> this->numerator;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '/');
std::cin >> this->denominator;
this->simplify();
}
void Fraction::simplify()
{
if ( this->denominator < 0 )
{
this->numerator *= -1;
this->denominator *= -1;
}
// x /= exp
// x = x / (exp)
FractionPart greatestCommonDivisor = Fraction::greatestCommonDivisor(this->numerator, this->denominator);
this->numerator /= greatestCommonDivisor;
this->denominator /= greatestCommonDivisor;
}
FractionPart Fraction::greatestCommonDivisor(FractionPart a, FractionPart b)
{
if ( a <= 0 )
a = -a;
if ( b <= 0 )
b = -b;
// Source: https://en.wikipedia.org/wiki/Euclidean_algorithm
while ( b )
{
FractionPart t = b;
b = a % b;
a = t;
}
return a;
}
Fraction Fraction::add(Fraction other)
{
Fraction result;
result.numerator = this->numerator * other.denominator + this->denominator * other.numerator;
result.denominator = this->denominator * other.denominator;
result.simplify();
return result;
}
Fraction Fraction::operator+(Fraction other)
{
return Fraction{
this->numerator * other.denominator + this->denominator * other.numerator
, this->denominator * other.denominator };
}