-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathejemplo05.c
205 lines (143 loc) · 6.85 KB
/
ejemplo05.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
205
/*
* C BY 8 EXAMPLES
* example05.c
* Copyright (C) 2019 Ignacio Pérez Hurtado de Mendoza
* http://www.cs.us.es/~ignacio
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// BIBLIOTECAS
#include <stdio.h>
#include <math.h>
// CONSTANTES
#define PI 3.14159265358979323846L
// TIPOS
typedef struct {
double x;
double y;
} PUNTO;
// CABECERA DE FUNCIONES
double distanciaEuclidea(PUNTO* a, PUNTO* b);
void intercambia(int* a, int* b);
int* malamente();
int* no_tan_malamente();
// VARIABLES GLOBALES
int variableGlobal = 101;
// FUNCION MAIN
int main()
{
// En C existe un tipo especial de datos denominado puntero
// que sirve para almacenar direcciones de memoria RAM
// Entendemos la RAM como un "casillero" en donde se almacenan
// datos, cada casilla tiene una direccion
// DIR DATO
// 0x100 x
// 0x104 y
// La unidad es el byte, en el ejemplo anterior, cada dato es de 4 bytes
// podemos definir una variable de tipo puntero añadiendo el asterisco *
// al tipo de dato referenciado por el puntero
// por ejemplo:
int* pt1;
// es un puntero para almacenar direcciones de memoria que contienen
// valores de tipo entero (4 bytes en la mayoria de las maquinas)
double* pt2;
// es un puntero para almacenar direcciones de memoria que contienen
// valores de tipo double (8 bytes)
// si tenemos una variable
int a = 10;
double b = PI;
// podemos obtener su direccion de memoria mediante el operador &
pt1 = &a; // punteros de tipo int* almacenan direcciones de memoria de variables int
pt2 = &b; // punteros de tipo double* almacenan direcciones de memoria de variables double
// pt1 ---> a
// pt2 ---> b
// para imprimir una direccion de memoria podemos usar el siguente formato
printf("La variable a tiene un valor %d y se encuentra en la direccion %p de la memoria RAM\n",a,pt1);
printf("La variable b tiene un valor %lf y se encuentra en la direccion %p de la memoria RAM\n",b,pt2);
// podemos acceder al valor de la variable apuntada por el puntero mediante el operador *
*pt1 = *pt1 + 2;
// El * en la anterior expresion es un operador unario para obtener el valor de la variable apuntada por el puntero
// no confundir con la declaracion del puntero int* pt1;
// aunque a veces veremos int *pt1; pero eso es porque sintacticamente es posible (al fin y al cabo los espacios no significan nada en C)
// ahora la variable a ha sido modificada
printf("Nuevo valor de a: %d\n",a);
printf("Tambien podemos consultar la variable usando su puntero: %d\n", *pt1);
// Un puntero no inicializado puede recibir el valor NULL
int *pt3 = NULL;
// que viene bien para chequear precisamente si ha sido inicializado o no
if (pt3 == NULL) {
printf("El puntero pt3 no está inicializado\n");
}
// en los ejemplos anteriores hemos visto funciones con argumentos que se pasan por valor, por ejemplo: int esPar(int x) recibe un valor que
// se copia en la variable local x. Esto no es recomendable para estructuras de datos o arrays (que veremos mas adelante), ya que la copia
// consume un tiempo O(n). En esos casos es recomendable pasar los argumentos por referencia, es decir usando punteros. Por ejemplo:
PUNTO u;
u.x = 10.4;
u.y = -23.4;
PUNTO v;
v.x = 0;
v.y = 6.4;
printf("Distancia entre u y v: %lf\n",distanciaEuclidea(&u,&v));
// Por regla general, los tipos de datos simples (char, short, int, long, float, double...) se pasan por valor. Las estructuras y arrays por referencia.
// Ya que podemos modificar una variable mediante su puntero, pasar un argumento a una funcion por referencia implica que ese argumento es de entrada/salida
// Asi que usaremos punteros en las funciones o bien para pasar estructuras y arrays o bien cuando queramos argumentos de entrada/salida
int x = 42;
int y = 24;
printf("x vale %d, y vale %d\n",x,y);
intercambia(&x,&y);
printf("x vale %d, y vale %d\n",x,y);
// las variables en C tienen "vigencia" en su ámbito, por ejemplo: cuerpo de una función, cuerpo de un bucle, cuerpo de un if-else. Un error muy habitual
// es usar el puntero a una variable que ha salido de su ámbito. Por ejemplo, que una funcion devuelva como resultado un puntero a una variable local.
// mirar la funcion "malamente".
// Descomenta la siguiente linea para disfrutar de tu primer "segmentation fault" o "violacion de segmento"
// int bienvenidos_a_vuestra_primera_violacion_de_segmento = *malamente();
// una violacion de segmento es un error muy habitual en C y suele ocurrir cuando se utiliza un puntero invalido, ya sea porque no se ha inicializado
// o porque hace referencia a una variable que ha salido de su ámbito. También puede ocurrir cuando se intenta acceder a zonas de memoria restringidas
// al usuario, como puede ser la memoria RAM que usa el sistema operativo.
printf("Variable global: %d\n",*no_tan_malamente());
// A continuacion una composición de punteros, un puntero que apunta a direcciones de memoria que contienen punteros que apuntan a direcciones de memoria que
// contienen valores de tipo entero:
int** pt4;
pt4 = &pt1;
// pt4 --> pt1 --> a
printf("El valor de la variable a traves de un puntero doble: %d\n",**pt4);
// Debido a que la memoria esta alineada, los punteros simples serviran tambien para referenciar la posicion de origen de un vector o array y los punteros
// dobles para matrices. Pero lo veremos en el siguiente ejemplo.
// Para leer un valor del teclado, se usa scanf
printf("Introduzca un valor numerico: ");
scanf("%d",&a);
printf("El valor introducido es %d\n",a);
// Observese que scanf es muy parecido a printf, pero scanf trabaja con direcciones de memoria
return 0;
}
// RESTO DE FUNCIONES
double distanciaEuclidea(PUNTO* a, PUNTO* b)
{
return sqrt((a->x - b->x) * (a->x - b->x) + (a->y - b->y) * (a->y - b->y));
// cuando tenemos un puntero a una estructura, se puede usar el operador -> en lugar del operador .
// por ejemplo: a->x es equivalente a (*a).x
}
void intercambia(int* a, int* b)
{
int aux = *a;
*a = *b;
*b = aux;
}
int* malamente()
{
int x = 42;
return &x; // MUY MAL! la variable x es local a la funcion y cuando se termina la funcion se libera memoria y el puntero no es valido
}
int* no_tan_malamente()
{
return &variableGlobal; // Las variables globales tienen vigencia en todo el programa
}