├── .gitignore ├── README.md ├── clase_datalab ├── Makefile ├── circular_shift_pizarra.pdf ├── ejercicios.c ├── funciones.h ├── numeros.c └── numeros.sh ├── ejemplos ├── Makefile ├── arreglosMalloc.c ├── do_while.c ├── estructuras.c ├── for.c ├── funciones.c ├── if_else ├── if_else.c ├── punteros.c ├── punterosYFunciones.c ├── size_t.c ├── strings.c ├── switch.c ├── typedef.c └── while.c ├── ejercicios └── guia.pdf └── img ├── C-compilation.png ├── arrays.png ├── arrays2.png ├── pointer-read.png └── pointer.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Nociones básicas de C 2 | ===================== 3 | 4 | Antes de empezar, deben saber que en el directorio **ejemplos** en este repo, disponen de varios programas pequeños que prueban la funcionalidad 5 | de algunas características primitivas del lenguaje. La idea es que vean el código, lo analicen y traten de predecir el resultado de su ejecución. Luego, pueden experimentar y modificar dichos archivos para hacer sus propias pruebas. Esa, consideramos, es la mejor manera de aprender. 6 | 7 | Para compilar los ejemplos, ya dentro del directorio `ejemplos`, alcanza con un: 8 | 9 | ```shell 10 | $ make all 11 | ``` 12 | 13 | o si quieren compilar algún ejemplo en particular, pueden tipear su nombre, por ejemplo: 14 | 15 | ```shell 16 | $ make strings 17 | ``` 18 | 19 | Para borrar los archivos generados, disponemos del ya clásico 20 | 21 | ```shell 22 | $ make clean 23 | ``` 24 | 25 | ## Tipos de datos 26 | 27 | Veamos los tipos de datos enteros nativos de C: 28 | 29 | |Tipo|Espacio ocupado| Rango || 30 | |---|---|---|---| 31 | |char|1 byte| -128 a 127 ó 0 a 255 |$-2^7$ a $2^7 - 1$ ó $0$ a $2^8 - 1$| 32 | |unsigned char|1 byte| 0 a 255 |$0$ a $2^8 - 1$| 33 | |signed char|1 byte| -128 a 127 |$-2^7$ a $2^7 - 1$| 34 | |short|2 bytes| -32768 a 32767 |$-2^{15}$ a $2^{15} - 1$| 35 | |unsigned short|2 bytes| 0 a 65535 |$0$ a $2^{16} - 1$| 36 | |int| usualmente 4 bytes| -2147483648 a 2147483647 |$-2^{31}$ a $2^{31} - 1$| 37 | |unsigned int| usualmente 4 bytes| 0 a 4294967295 |$0$ a $2^{32} - 1$| 38 | |long| usualmente 8 bytes| -9223372036854775808 a 9223372036854775807 |$-2^{63}$ a $2^{63} - 1$| 39 | |unsigned long| usualmente 8 bytes| 0 a 18446744073709551615 |$0$ a $2^{64} - 1$| 40 | 41 | Como vemos, no tenemos garantías absolutas del tamaño en bytes que va a ocupar cada tipo de datos y en el caso de un *plain char*, tampoco tenemos garantía de su *signedness* (signo). El tamaño es dependiente del compilador utilizado para una arquitectura determinada. Lo que sí podemos decir es que *generalmente* en arquitecturas de 64 bits, el **char** es **signed**, el **int** ocupa 4 bytes y el **long** ocupa 8 bytes. 42 | 43 | También y para evitar este problema, C dispone de tipos de longitud fija, que deben ser preferidos en donde esta ambigüedad de los largos nos pueda traer problemas. 44 | 45 | Para usarlos debemos incluir `stdint.h` (`#include `) 46 | 47 | Tipos signados: 48 | 49 | - **int8_t** (8 bits con signo) 50 | - **int16_t** (16 bits con signo) 51 | - **int32_t** (32 bits con signo) 52 | - **int64_t** (64 bits con signo) 53 | 54 | Tipos sin signo: 55 | 56 | - **uint8_t** (8 bits sin signo) 57 | - **uint16_t** (16 bits sin signo) 58 | - **uint32_t** (32 bits sin signo) 59 | - **uint64_t** (64 bits sin signo) 60 | 61 | Usando estos tipos **tenemos garantía** de que el tamaño es el especificado en el nombre del tipo. 62 | 63 | ### size_t 64 | Existe un tipo de datos llamado `size_t` que puede guardar el tamaño teórico máximo un objeto de cualquier tipo, incluyendo el tamaño de los arrays. Es de tipo **entero sin signo**. 65 | 66 | Se suele usar para indexar arrays. También es el tipo resultante del operador `sizeof` (operador que devuelve el tamaño en bytes de un objeto o tipo) 67 | 68 | Es una buena práctica usar `size_t` cuando declaramos variables para tamaños como el número de caracteres de un string, el tamaño de un array, etc. 69 | 70 | Ejemplo: 71 | 72 | ```C 73 | int array[34]; 74 | 75 | size_t len = sizeof(array) / sizeof(int); 76 | ``` 77 | 78 | En el ejemplo anterior, `len` vale 34, ya que el tamaño del array (en bytes) es: 34 * 4 y el tamaño de un `int` es 4 79 | 80 | ## Comentarios 81 | 82 | Se dispone de 2 tipos de comentarios: 83 | 84 | - En bloque: `` /* */ `` 85 | - En línea: ``//`` 86 | 87 | ```C 88 | /* Esto es un comentario en bloque, suele usarse para documentar funciones o módulos 89 | * completos. Los asteriscos agregados en el margen izquierdo son puramente cosméticos, 90 | * no cumple ninguna función sintáctica, pero se suelen usar porque quedan bien. Un ejemplo 91 | * abajo 92 | */ 93 | 94 | /* Función count_zeros 95 | * 96 | * array es un array de ints 97 | * length es la cantidad de elementos del array 98 | * 99 | * Devuelve la cantidad de ceros presentes en el array 100 | */ 101 | size_t count_zeros(int* array, size_t length){ 102 | size_t ceros = 0; // inicializo en 0 la cantidad de ceros 103 | for (size_t i=0; i 117 | tipo nombre_var = valor inicial; 118 | 119 | 120 | o 121 | 122 |
123 | tipo nombre_var;
124 | 
125 | 126 | 127 | 128 | En este segundo caso, puede contener basura dependiendo de donde la definamos 129 | 130 | Ejemplos: 131 | 132 | ```C 133 | uint64_t mi_var = 0; 134 | int mi_int = -985; 135 | uint8_t mi_byte = 24; 136 | 137 | int i; 138 | int j = i; 139 | ``` 140 | 141 | ### typedef 142 | 143 | Sirve para definir un nuevo tipo de dato con un nombre a elección a partir de un tipo de dato ya existente. Usualmente se lo utiliza para construir nombres más cortos o más significativos para tipos ya definidos por C o para tipos que hayan sido previamente declarados. 144 | 145 | 146 | La sintaxis es: 147 | 148 |
149 | typedef tipo nuevo_nombre;
150 | 
151 | 152 |
153 | 154 | Ejemplo: 155 | 156 | ```C++ 157 | int main(){ 158 | 159 |    typedef int edad_t; 160 | 161 |    edad_t edad_juan = 23; 162 |    printf("%d\n", edad_juan); 163 | 164 |    return 0; 165 | }  166 | ``` 167 | Este programa imprimirá una vez compilado y ejecutado: 168 | 169 | ```output 170 | 34 171 | ``` 172 | 173 | En definitiva `typedef` nos deja darle un significado declarativo a nuestro tipo de datos, que tiene que ver más con el uso que le estamos dando en nuestro programa. Además, si en algún momento queremos cambiar el tipo de por ejemplo, las variables que almacenan datos de edad, nos basta con cambiar la declaración en el `typedef` una sola vez. 174 | 175 | ## Constantes 176 | 177 | Hay 2 formas de definirlas: 178 | 179 | * ``const float PI = 3.1415926 `` 180 | * ``#define PI 3.1415926`` 181 | 182 | La segunda forma es un reemplazo de texto (donde dice PI en el código se reemplazará por el **texto** 3.1415926). Esto ocurre previo a la compilación, donde se invoca a una herramienta llamada **preprocesador**, Esta herramienta se encarga de hacer todos estos tipos de manipulaciones de texto, previo a la compilación. Su uso se denota porque la línea comienza con el carácter `#`. Otro ejemplo típoco donde actúa el preprocesador además de en los `#define` es en los `#include`, que básicamente reemplazan esa línea con el contenido del archivo que se incluye. 183 | 184 | ## Operadores 185 | 186 | | Operador | Significado | 187 | |---|---| 188 | | ``=`` | Asignación| 189 | |``+ `` |Suma | 190 | | ``-`` | Resta| 191 | | ``sqrt()`` | Raíz cuadrada (debe incluirse `math.h`) 192 | | ``*``  | Multiplicación| 193 | | ``/`` | División| 194 | | ``%`` | Módulo| 195 | 196 | 197 | * `` x (+, - , * , /)= y ``: Es lo mismo que hacer ``x = x (+, - , * , /) y`` 198 | 199 | Ejemplo: 200 | ```C 201 | x += y; // lo mismo que x = x + y; 202 | x /= y; // lo mismo que x = x / y; 203 | ``` 204 | 205 | * `x % y`: Es el resto de la división entre `x` e `y`. 206 | + `x / y`: Si los dos valores son int, es una división entera. Si alguno de los dos es un float, hace la división con decimales. Si no queremos hacer la división entera y tenemos dos int, podemos castear alguno de los dos a `float` o `double` y hacemos la división. Ejemplo: `float(6) / 10 `. También podemos hacer: `(1.0 * 6) / 10`. Tener en cuenta que esa constante `1.0` se interpreta como un `double` por default. Si queremos que se interprete como un float, podemos escribir `1.0f`. 207 | 208 |
209 | 210 | ## Control de flujo 211 | ### Operadores lógicos 212 | 213 | | Operador | Significado | 214 | |---|---| 215 | |``==``  | Igual| 216 | | ``!=`` | Distinto| 217 | | ``&&`` | AND | 218 | |``!`` | NOT | 219 | | \|\| | OR | 220 | | ``>``   | Mayor| 221 | |``<``   | Menor| 222 | | ``>=`` | Mayor o igual| 223 | | ``<=``| Menor o igual| 224 | 225 | Algo a tener en cuenta: los operadores lógicos en C devuelven `0` si el resultado es falso y devuelven `1` si el resultado es verdadero. Nativamente, C no dispone de tipos booleanos del estilo `true` y `false` (como por ejemplo C++ que dispone del tipo `bool`) 226 | 227 | 228 | ### If 229 | La sintaxis es: 230 | 231 |
232 | if(condición){
233 |     código
234 | }
235 | 
236 | 237 | ### if - else 238 | 239 | La sintaxis es: 240 | 241 |
242 | if(condición){
243 |     código
244 | }
245 | else if (condición){
246 |   código
247 | }
248 | else{
249 |   código
250 | }
251 | 
252 | 253 |
254 | 255 | ### switch - case 256 | 257 | Es una estructura de control que me permite elegir entre varias posibilidades según ciertas condiciones. 258 | 259 | Este va a evaluar el valor de la variable en cada `case` y si la condición se cumple, entra en dicho `case` y ejecuta ese bloque y TODOS los que le siguen. Este comportamiento se denomica *fall-through*. Si queremos terminar el case y salir, debemos poner un `break` explícitamente. 260 | 261 | En la condición tenemos que poner valores que tengan sentido con respecto al valor de dicha variable, sino nos tirará un error. Sólo funciona con tipos cuya representación sea entera. 262 | 263 | La sintaxis es: 264 | 265 |
266 | switch(variable)
267 | {
268 |   case valor_1:
269 |     código
270 |   case valor_2:
271 |     código
272 |   default: 
273 |     este caso es por si no matchea ninguno de los anteriores
274 | }
275 | 
276 | 277 | Ejemplo: 278 | 279 | ``` C++ 280 | int main(){ 281 | 282 |    int dia = 2; 283 | 284 |    switch (dia) { 285 | 286 |    case (0): 287 |    case (1): 288 |        printf("lunes\n"); 289 | 290 |    case (2): 291 |        printf("martes\n"); 292 | 293 |    case (3): 294 |        printf("miercoles\n"); 295 |        break; 296 | 297 |    default: 298 |        printf("jueves\n"); 299 | 300 |    } 301 | 302 |    return 0; 303 | 304 | } 305 | ``` 306 | Repetimos: ningún case tiene un break implícito. Si se quiere modificar ese comportamiento se debe utilizar `break` (este cortará el `switch` por completo). 307 | 308 | En el ejemplo, vemos que hay un *fall-through* desde el `case (2)` hasta el `case(3)` 309 | 310 | 311 | ```output 312 | martes  313 | miercoles 314 | ``` 315 | 316 | ### Operador Ternario 317 | Sirve para cuando tenemos un solo `if` y un ``else``. Lo que hace es si la ``condición`` es **verdadera* devuelve la `expresión1`, sino la ``expresión2``.  318 | 319 | Su sintaxis es: 320 | 321 |
322 | condición ? expresión1 : expresión2;
323 | 
324 | 325 | Ejemplo: 326 | 327 | ```C++ 328 | int main(){ 329 | 330 |    int i = 2; 331 |    int j = 5; 332 | 333 |    int k = i < j ? 1 : 0; 334 |    int m = i > j ? 1 : 0; 335 | 336 |    printf("%d\n", k); 337 |    printf("%d\n", m); 338 | 339 |    return 0; 340 | 341 | } 342 | ``` 343 | Salida: 344 | ```output 345 | 1 346 | 0 347 | ``` 348 | 349 | Curiosamente las líneas 350 | 351 | ```C 352 | int k = i < j ? 1 : 0; 353 | int m = i > j ? 1 : 0; 354 | 355 | ``` 356 | 357 | podrían haberse escrito también como: 358 | ```C 359 | int k = i < j; 360 | int m = i > j; 361 | ``` 362 | 363 | ¿Por qué? 364 | 365 | ## Ciclos 366 | 367 | ### Instrucciones de corte 368 | * break  369 | * continue 370 | * return 371 | 372 |
373 | 374 | ### For 375 | 376 | La sintaxis es: 377 | 378 |
379 | for(inicialización de variable; condición; incremento de variable)
380 | {
381 |   código
382 | }
383 | 
384 | 385 | Cualquiera de los segmentos puede dejarse en blanco. 386 | 387 | La ``inicialización de variable`` se ejecuta una sola vez, luego evalúa la ``condición``. La `condición` se evalúa en todo ciclo, incluyendo el primero y en general es la responsable de que el ciclo termine (salvo que haya un `break`). Si esta se cumple se ejecuta el bloque y luego el `incremento de variable`. Esto se repite hasta que la condición sea falsa y ahí sale del ciclo. 388 | 389 | Puede introducirse más de una `inicialización de variable` o más de un `incremento de variable` separando las sentencias por coma ``,``. 390 | 391 | Ejemplo: 392 | 393 | ``` C++ 394 | for (int i = 0, j = 1; i<10 && j<5 ; i++, j++) { 395 |  ... 396 | } 397 | ``` 398 | 399 | ### While 400 | 401 | La sintaxis es: 402 | 403 |
404 | while(condición)
405 | {
406 |   código
407 | }
408 | 
409 | 410 | ### Do - While 411 | 412 | La sintaxis es: 413 | 414 |
415 | do
416 | {
417 |   código
418 | }while(condición);
419 | 
420 | 421 |
422 | 423 | ## Funciones 424 | 425 | Prestar especial atención en la siguiente sección la diferencia entre *declaración* y *definición* 426 | 427 | ### Declaración de funciones 428 | 429 | La sintaxis es: 430 | 431 |
432 | tipo nombreFuncion (tipo arg1 , ...);
433 | 
434 | 435 | Las funciones, en la mayoría de los casos, deben ser declaradas antes de ser utilizadas. Los nombres de los argumentos son opcionales en las declaraciones. 436 | Indicamos que es una declaración porque termina en `;`. Una vez declarada, nos queda definirla. 437 | 438 | ### Definición de funciones 439 | 440 | La sintaxis es: 441 | 442 |
443 | tipo nombreFuncion (tipo arg1 , ...)
444 | {
445 |     código
446 | }
447 | 
448 | 449 | Ejemplo: 450 | 451 | ``` C++ 452 | int cuad(int n) { 453 |   return n * n; 454 | } 455 | ``` 456 | 457 | Si en una función no queremos que devuelva nada utilizamos como tipo de dato void: 458 | 459 | ```C++ 460 | void func() { 461 | 462 |    printf("%d\n", 5); // no devuelve nada 463 | 464 | } 465 | ``` 466 | Salida: 467 | ```output 468 | 5 469 | ``` 470 | 471 | Una cuestión técnica: las funciones no necesitan ser definidas para que el archivo pueda compilar correctamente, solo necesitan ser declaradas. Sin embargo, cuando querramos armar el archivo ejecutable, el linker buscará en todos los archivos objeto (los .o) que le pasemos por la definición de dicha función. En caso de no encontrar una definición, nos devovlerá un error del tipo `undefined reference` o similar. 472 | 473 | Si una función ha sido previamente declarada, debe ser definida con el mismo valor de retorno y tipos de argumentos, pero los nombres de los parámetros no necesitan ser los mismos, en la declaración y en la definición, aunque es recomendable que lo sean, por razones de claridad. 474 | 475 | Otra cosa a tener en cuenta es que una entidad en C puede ser declarada muchas veces pero sólo puede ser definida una sola vez. Las múltiples declaraciones que el compilador encuentre de la misma entidad tienen que ser compatibles entre sí. 476 | 477 | 478 | ### Ciclo de compilación 479 | 480 | Ahora que vimos funciones, podemos ver el proceso de compilación de C 481 | 482 | ![compilation](img/C-compilation.png) 483 | 484 | Como se puede observar, todo empieza con una serie de módulos `.c` y header files `.h`. El objetivo de los header files es contener sólo declaraciones, de manera que den a conocer ciertas entidades a los módulos que los incluyan (con la directiva de preprocesador `#include`). El preprocesador resolverá todas sus directivas (`#include`, `#define`, etc) y terminada su fase, entregará una serie de archivos .c al compilador. 485 | Estos archivos, son compilados uno a uno, por separado en un proceso conocido como *compilación separada*. 486 | 487 | Cada archivo compilado, genera un archivo objeto, convencionalmente con el mismo nombre, pero con extensión `.o`. Cada uno de estos archivos, hasta el momento, no sabe de la existencia del resto. Es más, podemos estar utilizando en un archivo una función o variable **definida** en otro. El compilador solamente mira que estemos haciendo un uso correcto de la función (que pasemos bien sus parámetros, en orden, tipo y número y que recibamos el valor de retorno con el tipo correcto). Para eso sólo necesita saber la "firma" o "prototipo" de una función, que es básicamente la información que nos brinda la **declaración** de la misma. 488 | 489 | El proceso continúa con el **linker**, que se encarga de armar un único archivo binario(ya sea un ejecutable o una biblioteca), a partir de todos los archivos `.o` y de referencias a funciones que se encuentren en otras bibliotecas, propias del sistema. Es en este momento, en donde se resuelve que el llamado de una función desde un archivo vaya al código contenido en el otro. El linker se encarga de armar y resolver todas las referencias a funciones y variables que hasta el momento venían siendo "promesas" de que se encontraban en otro lado. Una vez armado y resueltas todas las referencias, el linker ensambla un archivo ejecutable, listo para que el sistema operativo, a través un módulo llamado **loader**, pueda ejecutar el programa correctamente. 490 | 491 |
492 | 493 | ## Punteros 494 | 495 | Hemos llegado a una de las funcionalidades de C que lo que distingue de otros lenguajes: punteros. Pero, ¿Qué es un puntero? Básicamente, un puntero es una variable que almacena el valor de memoria de otra entidad. Esta otra entidad puede ser otra variable, una función, otro puntero, etc. Incluso puede almacenar posiciones de memoria *raw*, es decir, que el puntero per sé no tenga noción de a qué está apuntando, simplemente sabe que tiene una posición de memoria almacenada. Esa posición puede ser válida o inválida incluso, recae en nosotros saber a qué está apuntando realmente nuestro puntero. 496 | 497 | Ejemplo: 498 | 499 | ``` C++ 500 | int x = 4; // variable normal 501 | int *p = &x; // obtiene la dirección de memoria de x y la almacena en el puntero p 502 | ``` 503 | 504 | ![pointers](img/pointer.png) 505 | 506 | * `x` es una variable de tipo entera (`int`) que ocupa 4 bytes. 507 | * `p` es una varible de tipo puntero a entero (`int *`) que ocupa 8 bytes 508 | 509 | Notar que sin importar el tipo apuntado, los punteros ocupan SIEMPRE 8 bytes en una arquitectura de 64 bits. Esto es así porque las direcciones de memoria son de 8 bytes en dicha arquitectura. En una arquitectura de 32 bits, los punteros ocupan 4 bytes. 510 | 511 | ```C++ 512 | size_t size = sizeof(int*); // size es 8 en una arquitectura de 64 bits y 4 en una de 32 bits 513 | ``` 514 | 515 | ## Desreferenciación de punteros 516 | 517 | ``` C++ 518 | int y = *p + 6; // y resulta en el valor 10. Se usa el operador "*" para desreferenciar un puntero 519 | ``` 520 | 521 | ![pointers](img/pointer-read.png) 522 | 523 | * El asterisco desreferencia el puntero. Notar la diferencia en el uso del asterisco en la declaración y como operador. 524 | * El valor de la expresión `*p` es el valor de la variable cuya dirección de memoria es apuntada por el identificador. Se usa el operador `&`, *address-of*. 525 | 526 | En este caso p es un puntero que contiene la dirección de la variable `x`. Si quiero acceder al valor contenido en `x`, hago `*p` (tanto para lectura de dicho valor, como para escritura) 527 | 528 | Es decir, con un puntero podemos acceder indirectamente al valor de otra variable, tanto para leerlo, como para escribirlo. Esto último, tiene una utilidad subyacente muy grande: si queremos pasar a una función, un parámetro que en realidad es un objeto pesado (piensen en un array, por ejemplo), podemos pasar un puntero y entonces, ¡lo único que se va a copiar es el valor del puntero! La función puede acceder a los valores del array sin problemas, a través del puntero y nos evitamos tener que copiar un montón de memoria de manera ineficiente. 529 | 530 | ### Puntero a NULL 531 | 532 | ``` C++ 533 | int *px = NULL; 534 | ``` 535 | 536 | Básicamente, un puntero a NULL es un puntero que no apunta a nada. Es decir, no tiene una dirección de memoria válida. Esto es útil para inicializar punteros y luego chequear si apuntan a algo o no. Si no apuntan a nada, podemos decir que están *libres* y podemos asignarles una dirección de memoria válida. 537 | 538 | Un puntero a NULL y un puntero no inicializado son diferentes, ya que el puntero no inicializado puede apuntar a cualquier dirección de memoria, incluso a una dirección de memoria válida. En cambio, un puntero a NULL no apunta a nada. 539 | 540 | Notar que curiosamente, podemos inicializar un puntero con el valor 0, pero con ningún otro valor: 541 | 542 | ```C++ 543 | int *pi = 0; // es lo mismo que int *pi = NULL; 544 | pi = NULL // también es válido 545 | pi = 100; // esto no es válido 546 | pi = num; // esto tampoco es válido 547 | ``` 548 | 549 | Un ejemplo de cómo se puede usar el valor del puntero a NULL, sería: 550 | 551 | ```C++ 552 | if(pi){ 553 | // pi no es NULL 554 | } 555 | else{ 556 | // pi es NULL 557 | } 558 | 559 | ``` 560 | 561 | De esta manera, podemos saber si el puntero tiene un valor válido o no. 562 | 563 | No confundir NULL con el carácter nulo. El carácter nulo es el carácter `\0` y es un carácter como cualquier otro. NULL es un puntero que no apunta a nada. 564 | 565 | ### Puntero a void 566 | 567 | ``` C++ 568 | void* p = NULL; 569 | ``` 570 | 571 | Un puntero a void es un puntero que no tiene tipo. Es decir, no sabemos a qué tipo de dato apunta. Esto es útil para cuando queremos pasar un puntero a una función, pero no sabemos a qué tipo de dato apunta. Por ejemplo, si queremos pasar un puntero a una función que imprime la dirección de memoria a la que apunta, podemos hacer: 572 | 573 | ```C++ 574 | void print_value(void* p){ 575 | printf("%x\n", p); // Imprimimos la dirección a la que apunta el puntero p en hexadecimal 576 | } 577 | ``` 578 | 579 | Un puntero a void nunca será igual a ningún otro puntero. Sin embargo, dos punteros a void que apunten a NULL, serán iguales. 580 | 581 | ```C++ 582 | int num; 583 | int *pi = # 584 | printf("Valor de pi: %x\n", pi); // imprime la dirección de memoria de num en hexadecimal 585 | void *pv = pi; 586 | pi = (int*) pi; // cast a puntero a int 587 | printf("Valor de pi: %x\n", pi); // imprime la dirección de memoria de num en hexadecimal 588 | ``` 589 | 590 | Cuando ejecutamos esto, podemos comprobar que el valor de los punteros es el mismo: 591 | 592 | ```output 593 | Valor de pi: 7fffbf7c 594 | Valor de pi: 7fffbf7c 595 | ``` 596 | 597 | La lógica detrás del uso de punteros se entiende mejor a través de ejemplos concretos. Traten de mantener en mente estas nociones básicas y sigamos avanzando. Pronto se irán aclarando los conceptos. 598 | 599 | ## Arreglos 600 | ### Declaración de arreglos 601 | 602 | La sintaxis es: 603 | 604 |
605 | tipo de dato nombre_arr [ tamaño_arr ] = {lista inicialización}
606 | 
607 | 608 | Ejemplo: 609 | 610 | ``` C++ 611 | int mi_array[4] = { [1]=20, [3]=40 }; // define [0, 20, 0, 40] 612 | int mi_array[] = {0, 20, 0 , 40}; // define [0, 20, 0, 40] 613 | int mi_array[4] = {0, 20, 0 , 40}; // define [0, 20, 0, 40] 614 | 615 | int otro_array[4] = {}; // define [0, 0, 0, 0] 616 | ``` 617 | 618 | ![arrays](img/arrays.png) 619 | 620 | Otra forma equivalente de ver el mismo array en memoria sería: 621 | 622 | ![arrays](img/arrays2.png) 623 | 624 | 625 | ### Acceso a elementos del arreglo 626 | 627 | La sintaxis para acceder a los elementos de un array es: 628 | 629 |
630 | nombre_arr[index];
631 | 
632 | 633 | Ejemplo: 634 | 635 | ```C++ 636 | int arr[7]; 637 | 638 | for(size_t i=0; i<7; ++i){ 639 | arr[i] = i; 640 | } 641 | ``` 642 | 643 | ### Arreglos multidimensionales 644 | 645 | La sintaxis es: 646 | 647 |
648 | tipo de dato nombre_arr [ tamaño_arr1 ][ tamaño_arr2 ] = {lista inicialización}
649 | 
650 | 651 | Ejemplo: 652 | 653 | ```C++ 654 | int arr[2][3] = {{1,2,3}, {4,5,6}}; 655 | ``` 656 | 657 | Para declarar tomar como parámetro un array de dos dimensiones, se debe especificar el tamaño de la segunda dimensión. Esto es así porque el compilador necesita saber el tamaño de la segunda dimensión para poder calcular la dirección de memoria de cada elemento del array. Esto es así porque los arrays son contiguos en memoria, por lo tanto, si tenemos la dirección de memoria del primer elemento del array, podemos calcular la dirección de memoria del segundo elemento, sumando el tamaño del tipo de dato del array. Si tenemos la dirección de memoria del segundo elemento, podemos calcular la dirección de memoria del tercer elemento, sumando el tamaño del tipo de dato del array, y así sucesivamente. 658 | 659 | Un ejemplo de una función que recibe un array multidimensional sería: 660 | 661 | ```C++ 662 | void print_array(int arr[][3], size_t length){ 663 | for(size_t i=0; i 722 | char nombre_str [cantidad_elementos] = inicializador; 723 | 724 | 725 | El tamaño de un string es la cantidad de caracteres que tiene el string, más el caracter nulo `\0`. Por lo tanto, si queremos almacenar un string de 5 caracteres, tenemos que reservar 6 bytes de memoria. 726 | 727 | Ejemplo: 728 | 729 | ``` C++ 730 | int main(){ 731 | 732 |    char cadena[] = "hola"; // equivalente a {'h', 'o', 'l', 'a', '\0'} 733 | 734 |    printf("%s\n", cadena); 735 | 736 |    return 0; 737 | 738 | } 739 | ``` 740 | 741 | ```output 742 | hola 743 | ``` 744 | 745 | Notar que la notación entre comillas dobles `""` es equivalente a la notación entre llaves `{}`. Esto es así porque el compilador, cuando ve que estamos inicializando un array de caracteres con una cadena de caracteres, lo que hace es inicializar el array con los caracteres de la cadena, más el caracter nulo `\0`. 746 | 747 |
748 | 749 | Para comparar cadenas: 750 | 751 | ``` C++ 752 | char s1[] = "hola"; 753 | 754 | char s2[] = "hola"; 755 | 756 | if (strcmp(s1, s2) == 0) {...} 757 | 758 | ``` 759 | 760 | Cuando pasamos un array a una función, no es necesario que le pasemos el tamaño del array, ya que el array contiene el caracter nulo `\0` al final. Por lo tanto, la función puede saber cuál es el tamaño del array, leyendo el array hasta encontrar el caracter nulo `\0`. 761 | 762 | Ejemplo: 763 | 764 | ```C++ 765 | void print_string(char *str){ 766 | for(size_t i=0; str[i] != '\0'; ++i){ 767 | printf("%c", str[i]); 768 | } 769 | printf("\n"); 770 | } 771 | ``` 772 | 773 | Obviamente si el array no se encuentra bien formado, es decir, no tiene el caracter nulo `\0` al final, la función no va a saber cuál es el tamaño del array y va a seguir leyendo memoria hasta encontrar el caracter nulo `\0`. Esto es un comportamiento indefinido y puede llevar a errores de ejecución. 774 | 775 | 776 | ## Estructuras 777 | ### Definición de estructuras 778 | 779 | La sintaxis es: 780 | 781 | ``` C++ 782 | typedef struct s_nombreStruct { // s_ no es necesario pero se suele usar 783 | atributo_1; 784 | // ... 785 | atributo_n; 786 | } nombreStruct_t; 787 | ``` 788 | 789 | ### Declaración de punteros a estructuras 790 | 791 | La sintaxis es: 792 | 793 | ``` C++ 794 | struct_nombre_t* nombreInstancia = (struct_nombre_t*)malloc(sizeof(struct_nombre_t)); 795 | ``` 796 | 797 | ### Acceso a atributos a estructura (cuando no es puntero) 798 | 799 | La sintaxis es: 800 | 801 | ``` C++ 802 | nombreInstancia.structAtributo; 803 | ``` 804 | 805 | ### Acceso a atributos a puntero de estructura 806 | 807 | La sintaxis es: 808 | 809 | ``` C++ 810 | punteroInstancia -> structAtributo; 811 | ``` 812 | 813 |
814 | 815 | 816 | 817 | ## Malloc 818 | En C/C++, `malloc` es una función utilizada para asignar memoria dinámica en el heap durante el tiempo de ejecución de un programa. Permite reservar un bloque de memoria de tamaño específico y devuelve un puntero a la dirección base de ese bloque.¿ 819 | 820 | Existen varias razones por las cuales se utiliza `malloc` en C/C++: 821 | 822 | * **Ciclo de vida de los datos:** La memoria asignada con `malloc` persiste hasta que se libera explícitamente mediante la función `free`. Esto permite que los datos persistan más allá del ámbito de una función, lo que es útil cuando se necesita compartir datos entre diferentes partes del programa 823 | * **Tamaño de memoria dinámica:** A diferencia de la memoria estática (declarada en tiempo de compilación), la memoria asignada con `malloc` es dinámica y puede ajustarse durante la ejecución del programa. 824 | 825 | Es importante tener en cuenta que con la ventaja de la flexibilidad que proporciona `malloc`, también surgen responsabilidades adicionales. Cuando se utiliza `malloc`, el programador es responsable de liberar la memoria asignada cuando ya no es necesaria, utilizando la función `free`. Si no se libera la memoria adecuadamente, puede provocar pérdidas de memoria (memory leaks) y agotamiento de los recursos del sistema. Además, el uso incorrecto de `malloc` puede llevar a problemas de seguridad, como desbordamientos de búfer, si no se gestiona correctamente el tamaño y la manipulación de los datos almacenados en la memoria asignada. 826 | 827 | Casos: 828 | 829 | ```C++ 830 | int* func(int num){ 831 | 832 |   int* array[num]; 833 | 834 |   //... 835 | 836 |   return array; 837 | } 838 | ``` 839 | 840 | 841 |
842 | 843 | En el ejemplo hay 2 problemas: 844 | 845 | * Al no tener `array` un tamaño fijo, luego `num`puede tomar un valor muy grande que el stack no soporte. 846 | * Cómo `array` se inicializa en la función `func`, este se guarda en el stack relativo de esta función. Luego, después de hacer toda la ejecución, antes de retornar `array`, el stack elimina todo lo que  se definió durante la ejecución de esta función, por lo tanto, `array` se va a eliminar. Esto quiere decir que vamos a devolver un puntero que no existe. 847 | 848 | En síntesis, lo que sucede es que el puntero apunta a la dirección de memoria donde está ubicado el dato. Este a su vez, como se instanció en la función, se va a almacenar en el stack. No obstante, al finalizar la función, el stack libera todas las variables definidas dentro de ella. En consecuencia, si intentamos devolver ese puntero, se generará un error, ya que se trata de una dirección de memoria que ha sido liberada y, por ende, no se puede acceder a ella. 849 | 850 | Por lo tanto hay que declararla en el heap con Malloc: 851 | 852 | ``` C++ 853 | int* func(int num){ 854 | 855 |   int* array = malloc(sizeof(int) * num); 856 | 857 |   //... 858 | 859 |   return array; 860 | 861 | } 862 | ``` 863 | 864 | Si la queremos liberar en la ejecución de la misma función: 865 | 866 | ``` C++ 867 | void func(int num){ 868 | 869 |   int* array = malloc(sizeof(int) * num); 870 | 871 |   //... 872 | 873 |   free(array); 874 | 875 |   return; 876 | 877 | } 878 | ``` -------------------------------------------------------------------------------- /clase_datalab/Makefile: -------------------------------------------------------------------------------- 1 | CC=c99 2 | CFLAGS=-Wall -Wextra -z noexecstack -pedantic -Wno-unused-variable -Wno-unused-parameter -Wunused-but-set-variable -Wfloat-conversion -Wno-shift-negative-value -O0 -ggdb -no-pie -lm 3 | 4 | all: numeros funciones ejercicios 5 | 6 | # make numeros 7 | # ./numeros 8 | # para correr el programa de numeros.c 9 | numeros: numeros.c funciones.o 10 | $(CC) $(CFLAGS) $^ -o $@ 11 | 12 | funciones.o: funciones.c 13 | $(CC) $(CFLAGS) -c $< -o $@ 14 | 15 | # make ejercicios 16 | # ./ejercicios 17 | # para correr el programa de ejercicios.c 18 | ejercicios.o: ejercicios.c 19 | $(CC) $(CFLAGS) -c $< -o $@ 20 | 21 | clean: 22 | rm -f *.o 23 | rm -f numeros 24 | rm -f ejercicios 25 | -------------------------------------------------------------------------------- /clase_datalab/circular_shift_pizarra.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orgacomp/C/effec96fc94a91388e31d17e9c4583f3d766ce3b/clase_datalab/circular_shift_pizarra.pdf -------------------------------------------------------------------------------- /clase_datalab/ejercicios.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // sumBytePos - retorna la suma de cada byte en x si x es positivo, de lo contrario, 0 5 | // donde x es un entero (int) Ejemplos sumBytePos(0xF0000100) = 0, sumBytePos(0x00109A45) = 239 6 | // Operadores permitidos: ! ~ & ^ | + << >> 7 | 8 | 9 | int sumBytePos(int x) 10 | { 11 | int sign = (x >> 31); 12 | //x = x & ~sign; 13 | 14 | int byte0 = (x >> 0) & 0xFF; 15 | int byte1 = (x >> 8) & 0xFF; 16 | int byte2 = (x >> 16) & 0xFF; 17 | int byte3 = (x >> 24) & 0xFF; 18 | int suma = byte0 + byte1 + byte2 + byte3; 19 | return suma & ~sign; 20 | } 21 | 22 | // Calcula un circular shift de x en n bytes a la derecha si shift = 1, a la izquierda si shift = -1 23 | int circularShift(int x, int n, int shift) 24 | { 25 | // x un int de 32 bits, 0 res = 0xDEAAAAFA, res = 0xAAFADEAA 27 | 28 | // 0xFFFF FFFF => 0xFFF0 0000 29 | // 0xFFFF FFFF => 0xFFFF F000 => 0X0000 0FFF 30 | 31 | int bytes_si = (~0x0) << ((4 - n) << 3); // "separa" los bits para shift i 32 | 33 | int bytes_sd = ~(~0x0 << (n << 3)); // "separa" los bits para shift d 34 | 35 | int s_i_ad = x & bytes_si; 36 | 37 | int s_i_at = x & (~bytes_si); 38 | 39 | 40 | int s_d_at = x & bytes_sd; 41 | int s_d_ad = x & (~bytes_sd); 42 | 43 | int shift_d = (s_d_at << ((4 - n) << 3)) | ((s_d_ad >> (n << 3)) & ~bytes_si); 44 | int shift_i = (s_i_ad >> ((4 - n) << 3) & bytes_sd) | (s_i_at << (n << 3)); 45 | 46 | shift = shift >> 1; 47 | return (shift_i & shift) | (shift_d & (~shift)); 48 | } 49 | 50 | int main() 51 | { 52 | int x_sumbyte = 0x1B598412; // RESPUESTA: 10A 53 | printf("RES: %X\n", sumBytePos(x_sumbyte)); 54 | 55 | int x_shift = 0x109AAA45; // 45109AAA, RESPUESTA: 9AAA4510 56 | printf("RES: %X\n", circularShift(x_shift, 3, 1)); 57 | 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /clase_datalab/funciones.h: -------------------------------------------------------------------------------- 1 | #ifndef _FUNCS_H 2 | #define _FUNCS_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | char** read_from_file(char* filename); 9 | void write_from_file(char** file_data); 10 | 11 | #endif -------------------------------------------------------------------------------- /clase_datalab/numeros.c: -------------------------------------------------------------------------------- 1 | #include "funciones.h" 2 | #include 3 | #include 4 | #include 5 | 6 | void numbers_test() 7 | { 8 | // NUMERO ENTERO 3 - REPRESENTACION DECIMAL 9 | int num1 = 3; 10 | //printf("Numero decimal: %d\n", num1); 11 | 12 | 13 | // NUMERO ENTERO 3 - REPRESENTACION HEXADECIMAL 14 | int num2 = 0x00000003; 15 | //int num2 = 0x3; 16 | //printf("Numero hexadecimal: %d\n", num2); 17 | 18 | 19 | // REPRESENTACION BINARIA DE 3 20 | // 0000 0000 0000 0000 0000 0000 0000 0011 21 | // Es un numero de 32 bits == 4 bytes 22 | 23 | 24 | // NUMERO ENTERO -3 25 | int num3 = -3; 26 | //printf("Numero negativo: %d\n", num3); 27 | 28 | 29 | // REPRESENTACION BINARIA DE -3 30 | // 1111 1111 1111 1111 1111 1111 1111 1101 31 | 32 | // REPRESENTACION BINARIA DE -1 33 | // 1111 1111 1111 1111 1111 1111 1111 1111 34 | 35 | 36 | 37 | 38 | // MAXIMO VALOR DE UN INT 39 | int max_int = 0x7fffffff; 40 | // 2.147.483.647 41 | //printf("Maximo valor de un int: %d\n", max_int); 42 | 43 | // Su representacion binaria: 44 | // 0111 1111 1111 1111 1111 1111 1111 1111 45 | 46 | // Si sumamos 1, se convierte en el MINIMO VALOR de un int 47 | // 0111 1111 1111 1111 1111 1111 1111 1111 48 | // + 0000 0000 0000 0000 0000 0000 0000 0001 49 | // --------------------------------------- 50 | // 1000 0000 0000 0000 0000 0000 0000 0000 51 | 52 | // -2.147.483.648 53 | //printf("Minimo valor de un int: %d\n", max_int + 1); 54 | 55 | 56 | 57 | 58 | // OTRAS REPRESENTACIONES DE NUMEROS: UNSIGNED INT 59 | uint32_t num4 = 0x00000003; 60 | 61 | //printf("Numero 3 sin signo: %u\n", num4); 62 | // Representacion binaria: 63 | // 0000 0000 0000 0000 0000 0000 0000 0011 64 | 65 | uint32_t num5 = 0x7fffffff; 66 | // 2.147.483.647 67 | //printf("¿Maximo valor de un unsigned int? %u\n", num5); 68 | 69 | // Representacion binaria: 70 | // 0111 1111 1111 1111 1111 1111 1111 1111 71 | 72 | // Si sumamos 1, que pasa? 73 | 74 | // 0111 1111 1111 1111 1111 1111 1111 1111 75 | // + 0000 0000 0000 0000 0000 0000 0000 0001 76 | // --------------------------------------- 77 | // 1000 0000 0000 0000 0000 0000 0000 0000 78 | //printf("No se vuelve negativo: %u\n", num5 + 1); 79 | 80 | 81 | // MAXIMO VALOR DE UN UNSIGNED INT 82 | uint32_t max_uint = 0xffffffff; 83 | // 4.294.967.295 84 | //printf("Maximo valor de un unsigned int: %u\n", max_uint); 85 | 86 | // Representacion binaria: 87 | // 1111 1111 1111 1111 1111 1111 1111 1111 88 | 89 | // ¿Que pasa si sumamos 1? 90 | //printf("Maximo valor de un unsigned int + 1: %u\n", max_uint + 1); 91 | 92 | 93 | // 1111 1111 1111 1111 1111 1111 1111 1111 94 | // + 0000 0000 0000 0000 0000 0000 0000 0001 95 | // --------------------------------------- 96 | // 0000 0000 0000 0000 0000 0000 0000 0000 97 | 98 | 99 | 100 | 101 | // OTROS NUMEROS SIGNED & UNSIGNED 102 | 103 | int8_t int_8bits = 0x7f; 104 | //printf("Numero int de 8 bits: %d\n", int_8bits); 105 | // Representacion binaria: 106 | // 0111 1111 107 | 108 | //int8_t int_8bits_overflow = 0x80; 109 | // Representacion binaria: 110 | // 1000 0000 111 | 112 | uint8_t uint_8bits = 0xff; 113 | //printf("Numero uint de 8 bits: %u\n", uint_8bits); 114 | // Representacion binaria: 115 | // 1111 1111 116 | 117 | 118 | // OTROS NUMEROS SIGNED & UNSIGNED 119 | int16_t int_16bits = 0x7fff; 120 | //printf("Numero int de 16 bits: %d\n", int_16bits); 121 | // Representacion binaria: 122 | // 0111 1111 1111 1111 123 | 124 | uint16_t uint_16bits = 0xffff; 125 | //printf("Numero uint de 16 bits: %u\n", uint_16bits); 126 | // Representacion binaria: 127 | // 1111 1111 1111 1111 128 | 129 | int64_t int_64bits = 0x0000000000000003; 130 | //printf("Numero int de 64 bits: %ld\n", int_64bits); 131 | // Representacion binaria: 132 | // 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0011 133 | 134 | 135 | 136 | 137 | 138 | // OPERADORES LOGICOS! 139 | // AND => & 140 | // OR => | 141 | // XOR => ^ 142 | // NOT => ~ 143 | 144 | int32_t and_1 = 0x0f; 145 | // Representacion binaria: 146 | // 0000 0000 0000 0000 0000 0000 0000 1111 147 | 148 | int32_t and_2 = 0x03; 149 | // Representacion binaria: 150 | // 0000 0000 0000 0000 0000 0000 0000 0011 151 | 152 | // AND 153 | // 0000 0000 0000 0000 0000 0000 0000 1111 154 | // & 0000 0000 0000 0000 0000 0000 0000 0011 155 | // --------------------------------------- 156 | // 0000 0000 0000 0000 0000 0000 0000 0011 157 | 158 | int32_t and_result = and_1 & and_2; 159 | //printf("Numero 1: %d - Numero 2: %d - Resultado AND: %d\n", and_1, and_2, and_result); 160 | 161 | 162 | 163 | uint8_t or_1 = 0x07; 164 | // Representacion binaria: 165 | // 0000 0111 166 | 167 | uint8_t or_2 = 0x7f; 168 | // Representacion binaria: 169 | // 0111 1111 170 | 171 | // OR 172 | // 0000 0111 173 | // | 0111 1111 174 | // --------- 175 | // 0111 1111 176 | 177 | uint8_t or_result = or_1 | or_2; 178 | //printf("Numero 1: %u - Numero 2: %u - Resultado OR: %u\n", or_1, or_2, or_result); 179 | 180 | 181 | 182 | uint32_t xor_1 = 0x0000ffff; 183 | // Representacion binaria: 184 | // 0000 0000 0000 0000 1111 1111 1111 1111 185 | 186 | uint32_t xor_2 = 0xffffffff; 187 | // Representacion binaria: 188 | // 1111 1111 1111 1111 1111 1111 1111 1111 189 | 190 | // XOR 191 | // 0000 0000 0000 0000 1111 1111 1111 1111 192 | // ^ 1111 1111 1111 1111 1111 1111 1111 1111 193 | // --------------------------------------- 194 | // 1111 1111 1111 1111 0000 0000 0000 0000 195 | 196 | uint32_t xor_result = xor_1 ^ xor_2; 197 | //printf("Numero 1: %u - Numero 2: %u - Resultado XOR: %u\n", xor_1, xor_2, xor_result); 198 | 199 | 200 | 201 | int32_t not_1 = 0x0000000f; 202 | // Representacion binaria: 203 | // 0000 0000 0000 0000 0000 0000 0000 1111 204 | 205 | // NOT 206 | // 0000 0000 0000 0000 0000 0000 0000 1111 207 | // --------------------------------------- 208 | // 1111 1111 1111 1111 1111 1111 1111 0000 209 | 210 | int32_t not_result = ~not_1; 211 | //printf("Numero 1: %d - Resultado NOT: %d\n", not_1, not_result); 212 | 213 | 214 | 215 | 216 | // SHIFTS! 217 | 218 | // LEFT SHIFT => << 219 | // RIGHT SHIFT => >> 220 | 221 | int32_t left_shift = 0x0000000f; 222 | // Representacion binaria: 223 | // 0000 0000 0000 0000 0000 0000 0000 1111 224 | 225 | // LEFT SHIFT 226 | // 0000 0000 0000 0000 0000 0000 0000 1111 227 | // << 4 228 | // --------------------------------------- 229 | // 0000 0000 0000 0000 0000 0000 1111 0000 230 | 231 | int32_t left_shift_result = left_shift << 31; 232 | //printf("Numero: %d - Resultado LEFT SHIFT: %d\n", left_shift, left_shift_result); 233 | 234 | 235 | int32_t right_shift = 0xffff0000; 236 | // Representacion binaria: 237 | // 1111 1111 1111 1111 0000 0000 0000 0000 238 | 239 | // RIGHT SHIFT 240 | // 1111 1111 1111 1111 0000 0000 0000 0000 241 | // >> 12 242 | // --------------------------------------- 243 | // 0000 0000 0000 1111 1111 1111 1111 0000 244 | 245 | int32_t right_shift_result = right_shift >> 12; 246 | //printf("Numero: %d - Resultado RIGHT SHIFT: %d\n", right_shift, right_shift_result); 247 | //printf("Nuevo numero: %d - Resultado RIGHT SHIFT: %d\n", right_shift, right_shift >> 16); 248 | 249 | } 250 | 251 | 252 | int main(int argc, char *argv[]) 253 | { 254 | // PRUEBA DE NUMEROS - TIPOS DE DATOS Y REPRESENTACIONES 255 | numbers_test(); 256 | 257 | return 0; 258 | } 259 | -------------------------------------------------------------------------------- /clase_datalab/numeros.sh: -------------------------------------------------------------------------------- 1 | make clean 2 | make numeros 3 | echo -e "\nSalida de la ejecución:" 4 | ./numeros 5 | -------------------------------------------------------------------------------- /ejemplos/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-Wall -Wextra -pedantic -std=c11 -O0 -ggdb -no-pie 3 | LIBS:=-lm 4 | 5 | TARGETS:=typedef ifelse switch size_t for while do_while funciones estructuras strings arreglosMalloc punteros punterosYFunciones 6 | 7 | all: $(TARGETS) 8 | 9 | typedef : typedef.o 10 | $(CC) $(CFLAGS) $^ -o $@ 11 | 12 | typedef.o: typedef.c 13 | $(CC) $(CFLAGS) -c $^ -o $@ $(LIBS) 14 | 15 | ifelse : ifelse.o 16 | $(CC) $(CFLAGS) $^ -o $@ 17 | 18 | ifelse.o: if_else.c 19 | $(CC) $(CFLAGS) -c $^ -o $@ $(LIBS) 20 | 21 | switch : switch.o 22 | $(CC) $(CFLAGS) $^ -o $@ 23 | 24 | switch.o: switch.c 25 | $(CC) $(CFLAGS) -Wno-implicit-fallthrough -c $^ -o $@ $(LIBS) 26 | 27 | size_t : size_t.o 28 | $(CC) $(CFLAGS) $^ -o $@ 29 | 30 | size_t.o: size_t.c 31 | $(CC) $(CFLAGS) -c $^ -o $@ $(LIBS) 32 | 33 | for : for.o 34 | $(CC) $(CFLAGS) $^ -o $@ 35 | 36 | for.o: for.c 37 | $(CC) $(CFLAGS) -c $^ -o $@ $(LIBS) 38 | 39 | while: while.o 40 | $(CC) $(CFLAGS) $^ -o $@ 41 | 42 | while.o: while.c 43 | $(CC) $(CFLAGS) -c $^ -o $@ $(LIBS) 44 | 45 | do_while: do_while.o 46 | $(CC) $(CFLAGS) $^ -o $@ 47 | 48 | do_while.o: do_while.c 49 | $(CC) $(CFLAGS) -c $^ -o $@ $(LIBS) 50 | 51 | funciones: funciones.o 52 | $(CC) $(CFLAGS) $^ -o $@ 53 | 54 | funciones.o: funciones.c 55 | $(CC) $(CFLAGS) -c $^ -o $@ $(LIBS) 56 | 57 | estructuras: estructuras.o 58 | $(CC) $(CFLAGS) $^ -o $@ 59 | 60 | estructuras.o: estructuras.c 61 | $(CC) $(CFLAGS) -c $^ -o $@ $(LIBS) 62 | 63 | strings: strings.o 64 | $(CC) $(CFLAGS) $^ -o $@ 65 | strings.o: strings.c 66 | $(CC) $(CFLAGS) -c $^ -o $@ $(LIBS) 67 | 68 | arreglosMalloc: arreglosMalloc.o 69 | $(CC) $(CFLAGS) $^ -o $@ 70 | arreglosMalloc.o: arreglosMalloc.c 71 | $(CC) $(CFLAGS) -c $^ -o $@ $(LIBS) 72 | 73 | punteros: punteros.o 74 | $(CC) $(CFLAGS) $^ -o $@ 75 | punteros.o: punteros.c 76 | $(CC) $(CFLAGS) -c $^ -o $@ $(LIBS) 77 | 78 | punterosYFunciones: punterosYFunciones.o 79 | $(CC) $(CFLAGS) $^ -o $@ 80 | punterosYFunciones.o: punterosYFunciones.c 81 | $(CC) $(CFLAGS) -c $^ -o $@ $(LIBS) 82 | 83 | clean: 84 | rm -f *.o 85 | rm -f $(TARGETS) 86 | -------------------------------------------------------------------------------- /ejemplos/arreglosMalloc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | 6 | size_t tamanio; // Tamaño del arreglo 7 | do { 8 | printf("Ingrese un número positivo: "); 9 | scanf("%zu", &tamanio); 10 | } while (tamanio <= 0); 11 | 12 | int* arreglo = (int*)malloc(tamanio * sizeof(int)); // Asignamos memoria para el arreglo 13 | 14 | if (arreglo == NULL) { 15 | printf("No se pudo asignar memoria.\n"); 16 | return 1; // Salida con error 17 | } 18 | 19 | // Inicialización del arreglo con algunos valores 20 | for (size_t i = 0; i < tamanio; i++) { 21 | arreglo[i] = i * 10; 22 | } 23 | 24 | // Acceso e impresión de los valores del arreglo 25 | printf("Valores del arreglo:\n"); 26 | for (size_t i = 0; i < tamanio; i++) { 27 | printf("%d ", arreglo[i]); 28 | } 29 | printf("\n"); 30 | 31 | // Liberar la memoria asignada con malloc 32 | free(arreglo); 33 | 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /ejemplos/do_while.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | int numero; 5 | 6 | do { 7 | printf("Ingrese un número positivo: "); 8 | scanf("%d", &numero); 9 | } while (numero <= 0); 10 | 11 | printf("Ha ingresado el número positivo: %d\n", numero); 12 | 13 | return 0; 14 | } -------------------------------------------------------------------------------- /ejemplos/estructuras.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Definición de la estructura Auto 5 | typedef struct s_Auto { 6 | int anio; 7 | int velocidadMaxima; 8 | int precio; 9 | }Auto_t; 10 | 11 | // Función para inicializar un objeto Auto 12 | Auto_t* inicializarAuto(int anio, int velocidad, int precio) { 13 | Auto_t* autoObj = (Auto_t*)malloc(sizeof(Auto_t)); 14 | autoObj -> anio = anio; 15 | autoObj->velocidadMaxima = velocidad; 16 | autoObj->precio = precio; 17 | return autoObj; 18 | } 19 | 20 | // Función para mostrar los detalles de un objeto Auto 21 | void mostrarDetalles(const Auto_t* autoObj) { 22 | printf("Detalles del auto:\n"); 23 | printf("Año: %d\n", autoObj->anio); 24 | printf("Velocidad Máxima: %d km/h\n", autoObj->velocidadMaxima); 25 | printf("Precio: $%d\n", autoObj->precio); 26 | } 27 | 28 | 29 | int main() { 30 | // Inicialización utilizando la función 31 | Auto_t* autoObj = inicializarAuto(5, 200, 20000); 32 | 33 | // Acceso e impresión utilizando las funciones 34 | mostrarDetalles(autoObj); 35 | free(autoObj); 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /ejemplos/for.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | int numeros[] = {10, -20, 30, 40, 50}; 5 | 6 | printf("Elementos del arreglo:\n"); 7 | for (size_t i = 0, sumaPositivos = 0; i < sizeof(numeros) / sizeof(numeros[0]); ++i) { 8 | printf("%d ", numeros[i]); 9 | if (numeros[i] > 0) { 10 | sumaPositivos += numeros[i]; 11 | } 12 | printf("La suma de positivos hasta ahora es: %zu \n", sumaPositivos); 13 | } 14 | printf("\n"); 15 | return 0; 16 | } 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ejemplos/funciones.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //Declaración de funciones 4 | void imprimir(int); 5 | int cuad(int); 6 | int producto(int , int); 7 | 8 | //Implementacion de las funciones 9 | int cuad(int numero) { 10 | return numero * numero; 11 | } 12 | 13 | int producto(int numero1, int numero2) { 14 | return numero1 * numero2; 15 | } 16 | 17 | 18 | void imprimir(int numero) { 19 | printf("%d", numero); // no devuelve nada 20 | } 21 | 22 | int main(){ 23 | int numero1; 24 | printf("Ingrese un número: "); 25 | scanf("%d", &numero1); 26 | int numero2; 27 | printf("Ingrese otro número: "); 28 | scanf("%d", &numero2); 29 | printf("El cuadrado del producto entre %d y %d es: ", numero1, numero2); 30 | imprimir(cuad(producto(numero1, numero2))); 31 | printf("\n"); 32 | } 33 | -------------------------------------------------------------------------------- /ejemplos/if_else: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orgacomp/C/effec96fc94a91388e31d17e9c4583f3d766ce3b/ejemplos/if_else -------------------------------------------------------------------------------- /ejemplos/if_else.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | int numero; 5 | 6 | printf("Ingrese un número: "); 7 | scanf("%d", &numero); //lee los datos ingresados desde la entrada estándar 8 | 9 | printf("El número %d es ", numero); 10 | 11 | if (numero > 0) { 12 | printf("positivo.\n"); 13 | } else if (numero < 0) { 14 | printf("es negativo.\n"); 15 | } else { 16 | printf("cero.\n"); 17 | } 18 | printf("Además es %s. \n", numero % 2 == 0? "par" : "impar"); 19 | 20 | return 0; 21 | } -------------------------------------------------------------------------------- /ejemplos/punteros.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | int numero = 42; // Variable entera 5 | int* puntero; // Puntero a un entero 6 | 7 | puntero = № // Asignamos la dirección de 'numero' al puntero 8 | 9 | printf("Valor de 'numero': %d\n", numero); 10 | printf("Valor apuntado por 'puntero': %d\n", *puntero); // Accedemos al valor a través del puntero 11 | 12 | *puntero = 100; // Modificamos el valor a través del puntero 13 | 14 | printf("Nuevo valor de 'numero' despues de modificarlo a través de su puntero: %d\n", numero); // El valor de 'numero' cambió 15 | 16 | return 0; 17 | } -------------------------------------------------------------------------------- /ejemplos/punterosYFunciones.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Función que intercambia dos valores utilizando punteros 4 | void intercambiar(int* a, int* b) { 5 | int aux = *a; 6 | *a = *b; 7 | *b = aux; 8 | } 9 | 10 | int main() { 11 | int num1 = 5; 12 | int num2 = 10; 13 | 14 | printf("Antes del intercambio: num1 = %d, num2 = %d\n", num1, num2); 15 | 16 | // Llamada a la función para intercambiar los valores 17 | intercambiar(&num1, &num2); 18 | 19 | printf("Después del intercambio: num1 = %d, num2 = %d\n", num1, num2); 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /ejemplos/size_t.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | size_t count_zeros(int* array, size_t length){ 4 | size_t ceros = 0; // inicializo en 0 la cantidad de ceros 5 | for (size_t i=0; i 2 | #include 3 | 4 | int main() { 5 | // Declaración e inicialización de un string 6 | char mensaje[] = "Hola, alumnos de Organización del Computador!"; 7 | printf("Mensaje: %s\n", mensaje); 8 | 9 | // Obtener la longitud del string 10 | int longitud = strlen(mensaje); 11 | printf("Longitud: %d\n", longitud); 12 | 13 | // Copiar un string en otro 14 | char copia[100]; //asegurarse que entren todos los carácteres 15 | strcpy(copia, mensaje); 16 | printf("Copia: %s\n", copia); 17 | 18 | // Concatenar strings 19 | char saludo[] = " ¡Bienvenidos!"; 20 | strcat(mensaje, saludo); 21 | printf("Mensaje después de concatenar: %s\n", mensaje); 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /ejemplos/switch.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | int dia; 5 | printf("Ingrese el dia de la semana: "); 6 | scanf("%d", &dia); 7 | printf("La lista de actividades importantes del día:\n"); 8 | switch (dia) { 9 | case 1: 10 | printf("Descuentos en el super\n"); 11 | case 3: 12 | printf("hay clase de Organización del Computador!\n"); 13 | break; 14 | case 2: 15 | case 4: 16 | case 5: 17 | printf("hacer tp datalab!!\n"); 18 | break; 19 | default: 20 | printf("es finde!!\n"); 21 | } 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /ejemplos/typedef.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Definición de nuevos tipos de datos usando typedef 4 | typedef int Kilometros; 5 | typedef double Litros; 6 | 7 | int main() { 8 | // Declarar variables de los nuevos tipos definidos 9 | Kilometros distancia = 150; 10 | Litros combustible = 12.5; 11 | 12 | double consumoPorKilometro = combustible / distancia; 13 | 14 | printf("Distancia recorrida: %d km\n", distancia); 15 | printf("Combustible utilizado: %.2f litros\n", combustible); 16 | printf("Consumo por kilómetro: %.2f litros/km\n", consumoPorKilometro); 17 | return 0; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /ejemplos/while.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | int numero = 1; 5 | 6 | printf("Números pares del 1 al 10:\n"); 7 | while (numero <= 10) { 8 | if (numero % 2 == 0) { 9 | printf("%d ", numero); 10 | } 11 | numero++; 12 | } 13 | printf("\n"); 14 | 15 | return 0; 16 | } -------------------------------------------------------------------------------- /ejercicios/guia.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orgacomp/C/effec96fc94a91388e31d17e9c4583f3d766ce3b/ejercicios/guia.pdf -------------------------------------------------------------------------------- /img/C-compilation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orgacomp/C/effec96fc94a91388e31d17e9c4583f3d766ce3b/img/C-compilation.png -------------------------------------------------------------------------------- /img/arrays.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orgacomp/C/effec96fc94a91388e31d17e9c4583f3d766ce3b/img/arrays.png -------------------------------------------------------------------------------- /img/arrays2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orgacomp/C/effec96fc94a91388e31d17e9c4583f3d766ce3b/img/arrays2.png -------------------------------------------------------------------------------- /img/pointer-read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orgacomp/C/effec96fc94a91388e31d17e9c4583f3d766ce3b/img/pointer-read.png -------------------------------------------------------------------------------- /img/pointer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orgacomp/C/effec96fc94a91388e31d17e9c4583f3d766ce3b/img/pointer.png --------------------------------------------------------------------------------