Direccionamiento y Estructuras de Datos.

Documentos relacionados
Funciones. Diseño de funciones. Uso de instrucción jal y retorno de subrutina.

Ejercicios del Tema 3. Fundamentos de la programación en ensamblador

Generación de un ejecutable

Grupo de Arquitectura y Tecnología de Computadores (ARCOS)

Tema 13: Apuntadores en C

Tema 18: Memoria dinámica y su uso en C

UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA ELO311 Estructuras de Computadores

Estructuras de Computadores I Tarea 02 02/99

Algoritmo, Estructuras y Programación II Ing. Marglorie Colina

FUNDAMENTOS DE PROGRAMACIÓN. PRÁCTICA 11: Apuntadores

PUNTEROS (Apuntadores)

Programación. Test Autoevaluación Tema 3

Memoria Dinámica. Jornadas de Marzo 2010 Grupo de Usuarios de Linux Tania Pérez

Apuntadores (Punteros)

Ejercicios del Tema 3. Fundamentos de la programación en ensamblador

Programación en ensamblador Ejercicios resueltos

El operador contenido ( ) permite acceder al contenido de

Manejo de memoria en C

Un puntero no es más que una variable estática cuyo contenido es una dirección de memoria.

ESTRUCTURA DE DATOS. Memoria estática Memoria dinámica Tipo puntero Declaración de punteros Gestión de memoria dinámica Resumen ejemplo

Punteros. Índice. 1. Qué es un puntero y por que son importantes.

Punteros. Programación en C 1

PUNTEROS (APUNTADORES)

Curso de Programación en C. Licenciatura, FCQeI. APUNTADORES.

Apuntadores en C y C++

RESEÑA DE LENGUAJE C

Elementos de un programa en C

Tipos Recursivos de Datos

Analista Universitario en Sistemas. Taller de Programación II. Instituto Politécnico Superior MEMORIA DINAMICA

Funciones Definición de función

2. Variables dinámicas

Tema 05: Elementos de un programa en C

Estructuras de datos: vectores en C. Clase 7 y 8 Introducción a la Computación Patricia Borensztejn

REPRESENTACIÓN DE DATOS

Punteros y aritmética de punteros. se almacena el operando

void clear1( int array[], int size ) /* Versión con arreglos */ { int i; for( i = 0; i < size; i++) array[i] = 0; }

Programando sobre MIPS64

1.1 Tipos de Datos Primitivos. 1.2 Tipos de datos estructurados. 1.3 Definición de estructura de datos

Punteros. 1.- Paso de parámetros por referencia. Ya hablamos de ésto en funciones. 2.- Declaración de arreglos dinámicos.

Ejercicios del tema 4. El procesador

Programación I Teoría II.

2.2 Nombres, Ligado y Ámbito

Principios de Computadoras II

Es un lenguaje estructurado, tiene una abundante cantidad de operadores y tipos de datos.

Estructura de Computadores

Laboratorio 1 y 2: Programación en C, Assembler MIPS, y uso de Simulador MIPS32: PCSpim

Laboratorio de Arquitectura de Redes. Punteros en lenguaje C

Algoritmos y estructuras de datos

Algoritmos y Programación I

Procesador MIPS - Registros

Práctica 2 - Manejo de estructuras de datos y punteros

SESIÓN DE EJERCICIOS E1

Programación en C. Algoritmo y Estructura de Datos. Ing. M. Laura López. Programación en C

ESTRUCTURAS. Struct Identificador_ tipo_estructura { Tipo miembro_1; /*Declaración de los miembros*/

Universidad Autónoma del Estado de México 2016, Año del 60 Aniversario de la Universidad Autónoma del Estado de México

Estructuras de Computadores I Tarea 03 02/99. Manejo de Punteros. Compilación de funciones (manejo de frames).

Punteros. Lenguaje C ANSI

Tema 6: Memoria dinámica

Una expresión es una combinación de uno o más operandos y operadores para obtener un resultado.

Aprendiendo a programar Microcontroladores PIC en Lenguaje C con CCS

Tipos de datos y Operadores Básicos

Laboratorio de Arquitectura de Redes. Asignación dinámica de memoria en lenguaje C

Introducción al lenguaje C

Arquitectura del MIPS: Introducción

Apuntadores y Memoria dinámica. Apuntadores. M.C. Yolanda Moyao Martínez

TEMA 2. LENGUAJE C. CONCEPTOS BÁSICOS Y PROGRAMACIÓN ELEMENTAL.

Algoritmo, Estructuras y Programación I Ing. Marglorie Colina

Analista Universitario en Sistemas. Taller de Programación II. Instituto Politécnico Superior PUNTEROS

Arquitectura Interna del 8088

Programación Orientada a Objetos en C++

Sistemas Operativos Practica 1: procesos y concurrencia.

Cómo implementar tus propias funciones en Lenguaje C?

Práctica 3. Paso de parámetros entre subrutinas. 3. Consideraciones sobre el paso de parámetros

Contenido PARTE II: ESTRUCTURAS DE DATOS AVANZADAS

Asumiremos que se dispone de procedimientos para leer y escribir caracteres. Se desarrollan algunas funciones primitivas que nos serán útiles.

SESIÓN DE EJERCICIOS E1

Lenguaje C. República Bolivariana de Venezuela Fundación Misión Sucre Aldea Fray Pedro de Agreda Introducción a la Programación III

LENGUAJE. Tema 4 Vectores, Matrices y Cadenas de caracteres.

TEMA 0 Gestión de Memoria Dinámica

UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA ELO320 Estructuras de Datos y Algoritmos. 3. Estructuras. Tomás Arredondo Vidal

Informática I para Bachillerato

PUNTEROS EN C (APUNTADORES)

Fundamentos y Arquitectura de Computadores (ITTSE UV)

Organización procesador MIPS

05 Funciones en lenguaje C. Diego Andrés Alvarez Marín Profesor Asociado Universidad Nacional de Colombia Sede Manizales

Tema 6. Gestión dinámica de memoria

Cadena de caracteres. 1. Fundamentos de cadenas y caracteres

Guía práctica de estudio 04. Almacenamiento en tiempo de ejecución

Formato para prácticas de laboratorio

Programación I Funciones

Introducción a la Programación en C Funciones

Estructura de Computadores 2 [08/09] Entrada/Salida en procesadores MIPS

Curso de Programación Avanzada en C

Lenguaje de programación C. Introducción

funciones printf scanf

Este material es de uso exclusivo para estudio, los textos fueron tomados textualmente de varios libros por lo que está prohibida su impresión y

Conceptos de Arquitectura de Computadoras Curso 2015

Introducción a la Computación. Capítulo 10 Repertorio de instrucciones: Características y Funciones

Introducción al lenguaje C

Clases y Objetos en C++

Transcripción:

Capítulo 7. 1 Direccionamiento y Estructuras de Datos. Una vez conocidos los mecanismos de direccionamiento de un procesador es útil visualizar cómo se pueden emplear para manipular arreglos y estructuras. 7.1 Arreglos. Se requiere una variable entera sin signo, denominada el índice del arreglo, generalmente se emplea un registro. Y una zona de datos, palabras normalmente consecutivas en la memoria para almacenar las componentes. Todas las componentes tienen igual tamaño, y si se asume que están almacenadas en forma contigua, se tendrá que si se conoce la dirección del primero, la dirección de la componente i, queda dada por: Dirección del primero + i * (tamaño de la componente) Si el tamaño de la componente, que es una constante, es un múltiplo de dos, la multiplicación puede efectuarse como corrimientos a la izquierda. Esto también asume que el primer índice es cero. Este modelo de representación de los arreglos en assembler, es el que usa el lenguaje C, que emplea el nombre del arreglo como un puntero a la primera componente. Y que permite accesar a una componente vía indirección, con la expresión a+i, que es la dirección de la componente i del arreglo a. Notar que en C, la aritmética de punteros calcula de acuerdo al tamaño, la correcta dirección de la componente; sea ésta de una, media o varias palabras. Debido a la fórmula anterior, ubicar una componente cualquiera del arreglo, toma el mismo tiempo (el que demora en efectuarse una multiplicación o corrimiento más la suma), por esta razón a esta estructura se la denomina ram (Modela la memoria externa del computador. Esto asumiendo que el número de las componentes es elevado, y que no pueden emplearse registros, para representar el arreglo). 7.1.1. Ejemplo de manipulación de arreglos. En lenguaje C. El siguiente segmento, en C, describe la manipulación mediante acceso vía índice: int a[ ] = {0,1,2,3,4,5,6; int i = 0;

2 Estructuras de Computadores Digitales int k = 0; void main(void) { i = 5;... k = a[i]; /* lectura de componente de arreglo */... a[i] = k; /* escritura en componente de arreglo */ Notar que la definición del arreglo, se efectúa con una inicialización vía conjunto de componentes. En este caso no es preciso establecer el tamaño del arreglo. Si se hubiera definido: int arreglo[3]; se crea espacio para tres componentes de tipo entero: arreglo[0], arreglo[1], arreglo[2]. El mismo segmento anterior puede describirse empleando manipulación mediante acceso vía puntero: int a[ ] = {0,1,2,3,4,5,6; int i = 0; int k = 0; void main(void) { i = 5;... k = *(a+i); /* lectura de componente de arreglo */... *(a+i) = k; /* escritura en componente de arreglo */ En assembler. Se traslada a: main:.data a:.word 0,1,2,3,4,5,6 i:.word 0 k:.word 0.text.globl main #inicia variable i, en zona estática, con constante 5. li $t3, 5 # t3=5 Macro que puede escribirse: ori $t3,$zero,5 la $t0, i # t0=&i t0 es un puntero. Apunta a variable i. sw $t3, 0($t0) # *t0=t3 Escribe en lo que apunta t0. #las dos instrucciones anteriores pueden escribirse con la macro: sw $t3, i($zero) #para direccionar a[i].

Direccionamiento y estructuras de datos 3 #Primero se obtiene, en t2, el offset en bytes respecto al inicio. la $t0, i # t0 = &i lw $t0, 0($t0) # t0 = *t0 o también t0 = i sll $t2, $t0, 2 # t2 = i*4 # Luego se forma, en t2, la dirección de a[i] la $t1, a # t1 = &a addu $t2, $t2, $t1 # t2 = &(a[i] ) # Finalmente se deposita en t3 el contenido de a[i] lw $t3, 0($t2) # t3 = a[i] #Las tres instrucciones anteriores pueden escribirse con la macro: lw $t3, a($t2) # La asignación k=a[i] la $t4, k # t4 = &k sw $t3, 0($t4) # *t4 = t3 # Pueden ejecutarse otras instrucciones,... # En lo que sigue, se asume que se han modificado $t0 y $t2, o que se ha escrito en i # Para escribir componente (desde variable k) # Primero se deja el valor de la variable k en $t3. la $t4, k # t4 = &k lw $t3, 0($t4) # t3 = *t4 #Para direccionar a[i]: Primero se forma, en t2, offset en bytes. la $t0, i # t0 = &i sll $t2, $t0, 2 # t2 = i*4 # Luego se forma, en t2, la dirección de a[i] la $t1, a # t1 = &a addu $t2, $t2, $t1 # t2 = &(a[i] ) #Finalmente la asignación sw $t3, 0($t2) # *t2 = t3 jr ra Puede notarse en los comentarios el uso de los operadores * y &. Que básicamente permiten representar en forma abstracta el direccionamiento en base a registros. En operaciones intensivas con las componentes de un arreglo, conviene mantener el índice y la dirección de la componente a[i] en registros. Nótese que si las componentes fueran medias palabras, el corrimiento es sólo en una posición, para multiplicar por dos. En caso de bytes, no es necesario el paso de multiplicación, basta sumar el índice a la dirección inicial del arreglo. 7.1.2. Arreglos de caracteres. Strings. Un caso particular lo constituyen los arreglos de caracteres o strings. El cual es modelado, tanto en assembler como en C, mediante un puntero al primer carácter del string, y finalizando éste

4 Estructuras de Computadores Digitales mediante un carácter nulo ( 0x00), que se coloca automáticamente. Por ejemplo: = "Este es un string"; define e inicia el string, terminado con el carácter nulo. Veamos un ejemplo de diseño de función para la manipulación de strings: En lenguaje C. char *recortastring(register char *s, register int *inicial, register int *largo) { s = s + *inicial; *(s + *largo) = '\0'; return s; char *string Recorta largo carácteres de un string, desde la posición inicial ; y devuelve un puntero al string resultante. Se le pasa como argumentos la dirección del string y punteros a variables enteras que contienen la posición inicial y el largo. Entonces, un ejemplo de invocación es: pchar = recortastring(arr,&i,&l); Donde pchar es un puntero a char, arr es un string, i y l enteros, todas las variables deben ser visibles, en el momento de invocar a la función. En assembler. Pasando los argumentos en registros tipo a, y retornando resultado en registro v0: ################################################################## # v0 = recortastring($a0, $a1, $a2) # # char *recortastring(register char *s, register int *inicial, register int *largo) # ####################################################################### recortastring: subu $sp,$sp,4 # push ra sw $ra,0($sp) move $t0, $a0# t0 = s lw $t2, 0($a1) # t2 = *inicial addu $t0, $t0, $t2 # t0 = s + *inicial lw $t1, 0($a2) # t1 = *largo addu $t1, $t0, $t1 # t1 = s + *largo sb $zero, 0($t1) # *t1 = '\0' Coloca el carácter nulo de fin de string. move $v0, $t0 # vo = nuevo inicio del string recortado. return(s) lw $ra, 0($sp) # pop ra addu $sp, $sp, 4 jr $ra # retorna

Direccionamiento y estructuras de datos 5 7.1.3. Diferencia entre manipulación de arreglos vía índice y vía puntero. 7.1.3.1. Vía índices. Se tiene la siguiente función en C, que coloca en cero los elementos de un arreglo. void cleararray(int a[], int celdas) { int i; for( i = 0; i < celdas ; i++) a[i] = 0; Se diseña la subrutina en assembler, con el siguiente empleo de registros: #argumentos $a0 = a, $a1 = celdas #local $t0 = i #temporales $t1 = &a[i], $t2, $t3 cleararray: add $t0, $zero, $zero # i = 0 lazo: slt $t2, $t0, $a1 # t2 = 1 si i<celdas beq $t2, $zero, endfor add $t3, $t0, $t0 # t3 = 2*i add $t3, $t3, $t3 # t3 = 4*i add $t1, $a0, $t3 # t1 = & a[i] sw $zero, 0($t1) # *t1 = 0 addi $t0, $t0, 1 # i++ j lazo endfor: jr $ra Nótese que esta rutina no invoca a otras, por esto se denomina de tipo hoja. No es preciso salvar ra. Y como alcanzan los registros t, no es preciso emplear registros s; si los empleara debería salvarlos y restaurarlos. Debe observarse que el lazo es de 8 instrucciones, podría reducirse empleando un corrimiento lógico a la izquierda, en lugar de dos add. Cada vez debe calcularse &a[i] como a + i*4. Para comprobar el funcionamiento, se da un ejemplo de invocación en el siguiente main:.data a:.word 0,1,2,3,4.text.globl main main: la $a0,a li $a1,5 jal cleararray j main

6 Estructuras de Computadores Digitales 7.1.3.2. Vía punteros. Colocar en cero los valores del arreglo. Void clearptr1(int *a, int celdas) { int p; for( p = a; p < a+celdas ; *p = 0, p++); La rutina assembler emplea la siguiente asignación de registros: #argumentos $a0 = a, $a1 = celdas #locales $t0 = p #temporales $t1 = &a[celdas] = a+celdas, $t2 clearptr1 : add $t0, $zero, $a0 # t0 = p = &a[0] = a lazo : add $t1, $a1, $a1 # t1 = 2*celdas add $t1, $t1, $t1 # t1 = 4*celdas add $t1, $t1, $a0 # t1 = a+4*celdas slt $t2, $t0, $t1 # t2 = 1 si p<a+celdas beq $t2, $zero, endfor sw $zero, 0($t0) # *p = 0 addi $t0, $t0, 4 # p++ tipo de puntero j lazo endfor: jr $ra Nótese que se emplea aritmética de punteros para accesar los elementos del arreglo. Si el arreglo fuera de medias palabras, debe incrementarse p en dos; si fueran bytes en 1. En el lenguaje C, el compilador realiza los incrementos de acuerdo a los tipos de puntero. También se emplean ocho instrucciones en cada pasada por el lazo; sin embargo existe ahora una instrucción constante, que puede sacarse del lazo; ya que (a + celdas) es una constante. El operador coma, indica el orden en que se deben evaluar las instrucciones de la lista. Se utiliza esto para escribir dentro del for toda la acción. El siguiente ejemplo, en C, muestra la parte del código que no varía dentro del lazo (en la actualidad los compiladores optimizantes detectan los segmentos constantes, dentro de bloques y los extraen automáticamente.) 7.1.3.3. Versión dos, con manipulación vía punteros. void clearptr2(int *a, int celdas) { int *p,*q; for( p = a, q = a+celdas; p < q ; p++) *p = 0; En assembler, asignación de registros:

Direccionamiento y estructuras de datos 7 #argumentos $a0 = a, $a1 = celdas #locales $t0 = p, $t1 = q = a+celdas #temporal $t2 clearptr2: add $t0, $zero, $a0 # t0 = p = &a[0] = a add $t1, $a1, $a1 # t1 = 2*celdas add $t1, $t1, $t1 # t1 = 4*celdas add $t1, $t1, $a0 # t1 = q = a+4*celdas lazo: slt $t2, $t0, $t1 # t2 = 0 => fin del for beq $t2, $zero, endfor sw $zero,0($t0) # *p = 0 addi $t0, $t0, 4 # p++ j lazo endfor: jr $ra Ahora se advierte que la versión con punteros disminuye a 5 las instrucciones que se realizan cada vez en el lazo del for. Las ventajas temporales de esto se advierten si el número de celdas es elevado. Por esta razón se orienta a los programadores en C a emplear punteros, aunque el código no resulte sencillo de leer; por ejemplo la sentencia for podría haberse escrito según: for( p = a, q = a+celdas; p < q ; *p++=0) ; Esto descripción podría tener ventajas en procesadores con direccionamiento autoincrementado y escalado (caso del PENTIUM PRO). 7.1.4. Ordenar arreglos. Finalmente un ejemplo más complejo de tratamiento de arreglos. void burbuja(int *a, int size) { int i,j; for(i=0; i<size-1; i++) for(j=i+1; j<size; j++) if( a[i]> a[j] ) { swap(a,i,j); /*el más liviano asciende*/ Donde la rutina swap queda definida por: void swap(int *a, int i, int j) { int temp; temp = a[i]; a[i]=a[j]; a[j] = temp; El programa burbuja efectúa un ordenamiento ascendente de las componentes del arreglo. Dejando el menor en la primera posición. El siguiente programa muestra como probar el funcionamiento de la rutina burbuja:

8 Estructuras de Computadores Digitales.data a:.word 4,3,2,1,0.text.globl main main: la $a0,a # Paso de argumentos li $a1,5 jal burbuja stop: j stop #argumentos $a0 = a, $a1 = size #locales $t0 = i, $t1 = j #temporales $t2 = size-1, $t3, $t4, $t5, $t6, $t7 burbuja: for1: add $t0, $zero, $zero # t0 = i = 0 addi $t2, $a1, -1 # t2 = size-1 lazo1: slt $t3, $t0, $t2 # t3 = 1 si i<size-1 beq $t3, $zero, endfor1 for2: addi $t1, $t0, 1 # t1 = j = i+1 lazo2: slt $t3, $t1, $a1 # t3 = 1 si j<size beq $t3, $zero, endfor2 add $t3, $t0, $t0 # t3 = 2*i add $t3, $t3, $t3 # t3 = 4*i add $t3, $t3, $a0 # t3 = &a[i] add $t4, $t1, $t1 # t4 = 2*j add $t4, $t4, $t4 # t4 = 4*j add $t4, $t4, $a0 # t4 = &a[j] lw $t5, 0($t3) # t5 = *t3 = a[i] lw $t6, 0($t4) # t6 = *t4 = a[j] slt $t7, $t6, $t5 # t7 = 1 si t6<t5 a[i]>a[j] beq $t7, $zero, noswap swap: sw $t5, 0($t4) # a[j] = a[i] sw $t6, 0($t3) # a[i] = a[j] noswap:add $t1,$t1,1 # j++ j lazo2 endfor2: addi $t0, $t0, 1 # i++ j lazo1 endfor1: jr $ra Nótese el empleo de registros temporales para las locales y la extracción de la constante size-1 fuera del lazo. Se decidió no implementar el swap con invocación a una subrutina, ya que en el momento de invocar se tienen en registros los valores de las componentes y las direcciones donde debe guardárselas.

Direccionamiento y estructuras de datos 9 Compilando la rutina swap: En caso de efectuar una subrutina swap, como la mostrada, se requieren tres argumentos, por lo cual se derraman al stack a0 y a1. Si swap, empleara t0, t1, t2, entonces burbuja debe salvar t0, t1 y t2 antes de llamar a swap; el resto de los registros temporales empleados por burbuja, escriben antes de leer a los registros t3, t4, t5, t6 y t7, y por lo tanto no requieren ser salvados. Un análisis más detallado indicaría no emplear t0, t1 y t2 en la de swap, y no sería necesario salvarlos. Si se reemplazara la línea rotulada swap y la siguiente por jal swap, la zona antes y después del llamado debe modificarse según: addi $sp, $sp, -24 sw $ra, 20($sp) #esta podría haber estado al inicio de burbuja sw $a0, 16($sp) sw $a1, 12($sp) sw $t0, 8($sp) sw $t1, 4($sp) sw $t2, 0($sp) move $a0, $a0 #en este caso es la misma. No es necesaria move $a1, $t0 move $a2, $t1 jal swap lw $ra, 20($sp) lw $a0, 16($sp) lw $a1, 12($sp) lw $t0, 8($sp) lw $t1, 4($sp) lw $t2, 0($sp) addi $sp, $sp, 24 Lo cual muestra el costo de invocar a subrutinas. Además de la ejecución de la subrutina, debe procederse a salvar registros (7 instrucciones), más la carga de argumentos (3 instrucciones), más el jal, más el código de restauración (7 instrucciones), más el texto de la subrutina (11 instrucciones); en total 29 instrucciones, en lugar de las dos necesarias sin emplear subrutinas. Se muestra a continuación la compilación del código en C, para swap. #argumentos $a0 = a, $a1 = i, $a2 = j #local $t0 = temp #temporales $t1 = &a[i], $t2=&a[j], $t3 swap: add $t1,$a1,$a1 # t1 = 2*i add $t1,$t1,$t1 # t1 = 4*i add $t1,$a0,$t1 # t1 = & a[i] add $t2,$a2,$a2 # t2 = 2*j add $t2,$t2,$t2 # t2 = 4*j add $t2,$a0,$t2 # t2 = & a[j]

10 Estructuras de Computadores Digitales lw $t0,0($t1) # temp = a[i] lw $t3,0($t2) # t3 = a[j] sw $t3,0($t1) # a[i] = a[j] sw $t0,0($t2) # a[j] = temp jr $ra La subrutina swap es hoja, no requiere salvar ra, y sólo emplea temporales. El algoritmo que se emplee, puede simplificar o complicar el texto de los programas. Por ejemplo se tiene la siguiente alternativa para burbuja y swap que tiene un menor costo, ya que intercambia posiciones adyacentes. Sólo es necesario calcular la dirección de a[j], ya que el próximo en el arreglo, se logra con direccionamiento con offset; esto implica ahorrar tres instrucciones (las que calculan &a[j+1]). Si en t5 se desea dejar a[j], y en t3 se tiene &a[j], el código podría ser: lw $t5, 0($t3) # t5 = *t3 = a[j] lw $t6, 4($t3) # t6 = a[j+1] void burbuja2(int *a, int size) { int i,j; for(i=0; i<size; i++) for(j=i-1; j>=0&& a[j]>a[j+1]; j--) { swap2(a,j); Donde la rutina swap queda definida por: void swap2(int *a, int k) { int temp; temp = a[k]; a[k]=a[k+1]; a[k+1] = temp; Como podrá estudiarse en un curso de estructuras de datos y algoritmos, el costo de la subrutina burbuja es proporcional a n al cuadrado; donde n es el número de componentes del arreglo. Existen otros algoritmos como el heapsort y quicksort que son proporcionales a n*logn, con el logaritmo en base dos. Para valores de n elevados, la elección del algoritmo es fundamental en el tiempo de ejecución. 7.2. Estructuras. Struct. El tamaño de las componentes es variable. La dirección del primer campo está dada por: Dirección de inicio de la estructura. La dirección del segundo campo está dada por: Dirección de inicio de la estructura + tamaño del primer campo.

Direccionamiento y estructuras de datos 11 La dirección del tercer campo está dada por: Dirección de inicio de la estructura + suma de los tamaños del primer y segundo campo. Y así sucesivamente. Para simplificar el ejemplo, se asumen dos campos de igual tamaño. Ejemplo. En lenguaje C. struct punto { int x; int y; ; struct punto a = { 1, 2; /*se inicializan al definir el espacio */ struct punto b = { 3, 4; void main(void) { a.x=b.x; a.y=b.y; /*Se puede escribir a= b; */ En asembler. Se traslada a:.data structa:.word 1.word 2 structb:.word 3.word 4 main:.text.globl main #apuntar a estructuras la $t0,structb la $t1,structa lw $t3,0($t0) #t3=b.x sw $t3,0($t1) lw $t4,4($t0) #t4=b.y sw $t4,4($t1) jr ra #retorna del main.

12 Estructuras de Computadores Digitales El ejemplo usado de estructura es muy simple. Sin embargo lo esencial es darse cuenta que el direccionamiento es de registro base con offset. Y que se emplea el registro base apuntando al inicio de la estructura, y mediante el offset se accesa a los campos. En el caso de arreglos el registro base apunta a la componente del arreglo, y el offset es cero, para accesar a una componente genérica A[i]. Sólo si el acceso es a una componente específica, por ejemplo A[5], podría emplearse el offset para establecer el número de bytes, con respecto al registro base, que en este caso debe apuntar al inicio del arreglo. 7.3. Más sobre Manejo de Strings. (Direccionamiento de bytes) Se tiene la rutina strcpy para copiar un string apuntado por from hacia un espacio apuntado por to. Strcpy está definida en una de la bibliotecas estándar de C, para usarla basta incluir <string.h>. Como en el programa que se ilustra más abajo se la redefine, no se usará la estándar de la biblioteca, sino la redefinida en el programa. Para comprobarlo, puede quitar el comentario dentro de strcpy. Notar el efecto secundario que tienen los operadores de postincremento en C; para ello se agrega una línea, como comentario, que es equivalente a la acción del while. También debe observarse que en C la asignación es una expresión; se evalúa el lado izquierdo y se lo asigna al left-value (dirección de la memoria, que almacena una variable) y al mismo tiempo es el valor de la expresión. Y como en el texto esa expresión es una condición (igual a cero, es valor falso; diferente de cero es valor verdadero), se ha descompuesto en una comparación con el valor NULL, y una asignación después del lazo while; ya que en el código del programa, primero se efectúa la asignación y luego se evalúa la condición. Esta estructura del lenguaje, permite escribir código que puede resultar difícil de entender, para una persona que se esté iniciando en el manejo del lenguaje. El siguiente programa escrito en C: #include <stdio.h> char *strcpy(register char * to, register char * from) { register char *cp; /*se requiere cp ya que se retorna el argumento*/ cp = to; while( *cp++ = *from++ ) continue; /*while(*from!= NULL) {*cp =*from; cp++; from++; *cp=*from; */ /*printf( pasé por aquí..\n ); */

Direccionamiento y estructuras de datos 13 return(to); char *a="primer mensaje\n"; char *b="segundo mensaje\n"; int *c; int main(void) { printf("%s ",a); printf("%s ",b); c=strcpy(b,a); printf("%s ",a);printf("%s ",b); return (1); Se traslada según:.data stringa:.asciiz "primer mensaje\n" stringb:.asciiz "segundo mensaje\n" c:.word 0 #el puntero a char se trata como una dirección de 32 bits. main:.text.globl main addiu $sp, $sp,-4 #push ra sw $ra, 0($sp) #salva dirección de retorno. Main invoca a subrutina. la jal la jal $a0,stringa prints $a0,stringb prints la $a0,stringb #to b la $a1,stringa #from a jal strcpy la $t0,c #t0=&c sw $v0,0($t0) #*t0=v0 la jal la jal $a0,stringa prints $a0,stringb prints li $v0,1 #return(1) lw $ra, 0($sp) #restarura ra addiu $sp, $sp, 4 #pop jr $ra #retorna.

14 Estructuras de Computadores Digitales ############################################################ # $v0 = strcpy( $a0,$a1) # ############################################################ #copia string apuntado por a1 en dirección apuntada por a0 strcpy: subu $sp, $sp, 4 # push ra sw $ra, 0($sp) move $t0, $a0 # t0 es cp # while(*$a1!= NULL) {*$t0=*$a1;$t0++;$a1++; while: lb $t2, 0($a1) # t2=*a1 beq $t2, $zero,salirstr sb $t2, 0($t0) # *t0=*a1 addiu $t0, $t0, 1 addiu $a1, $a1, 1 j while salirstr: move $v0, $a0 # return($a0) lw $ra, 0($sp) # restaura addu $sp, $sp, 4 jr $ra # retorna de strcpy #*********************************************************************** # prints("%s",$a0); # #*********************************************************************** #imprime string apuntado por $a0. prints: addiu $sp, $sp,-12 #crea espacio del frame de 12 bytes. sw $ra, 0($sp) #salva dirección de retorno #debug para ver argumento de prints en stack sw $a0, 4($sp) #salva arg li $v0,4 #imprime string en $a0 syscall lw $ra, 0($sp) addiu $sp, $sp, 12 jr $ra #y retorna. Cuando se ejecuta paso a paso la rutina prints, aparentemente el stack no se modifica, esto debido a que el simulador no actualiza la zona de stack hasta que se escriba algo en ella. Por esta razón se agrega una escritura en el stack (que el programa no necesita); esto se efectúa con la línea que comienza con la palabra debug (que puede traducirse como depurar; literalmente significa sacar los bichos).

Direccionamiento y estructuras de datos 15 7.4 Manejo dinámico de la memoria. El siguiente segmento en C, emplea las funciones malloc y free, que permiten durante la ejecución de un programa solicitar espacio de memoria y liberarlo. El argumento de malloc (memory allocation), mediante la función estándar sizeof determina el número de bytes que se solicitaran al administrador de memoria (parte del sistema operativo). Si es posible asignar espacio retorna un puntero a carácter del primer byte asignado; en caso contrario retorna Null. El administrador dinámico mantiene datos para asignar y desasignar segmentos de memoria en una zona denominada heap. El código siguiente ilustra la forma de pedir espacio para un entero y luego libera el espacio asignado. Debe notarse que la única forma de acceder al espacio asignado es mediante un puntero (punt en el ejemplo) ya que no se conoce la dirección, que en el momento de ejecución le será asignada al entero. #include <stdio.h> #include <stdlib.h> /* para incluir prototipos de malloc y free */ typedef int *pi; /*Definición de tipo. Puntero a integer. */ #define Nulo (int *)0; int main(void) { pi punt=nulo; /*Define e inicia puntero*/ punt punt = (pi) malloc(sizeof(int)); /*pide espacio y lo encadena. */ if (punt ==Nulo) return(1); /* si no hay espacio */ *punt=5; printf("%d\n",*punt); free(punt); return(0); punt /*Libera espacio*/ 5 Pertenece al segmento de datos denominado heap. variable en el frame de main. Figura 7.1 Accesos vía punteros.

16 Estructuras de Computadores Digitales El siguiente segmento assembler emplea el llamado 9 al sistema, para solicitar memoria (sbrk). Se pasa en a0 el número de bytes que se piden. Retorna en v0 un puntero al espacio asignado. Se escribe una configuración de bits, en la zona asignada para poder visualizarla en el segmento de datos. Debe notarse que por defecto el inicio del heap es 0x0x10040000, y los sucesivos requerimientos son asignados a continuación de lo ya ocupado. No existe un llamado para liberar el espacio; por lo tanto no es posible recuperar las celdas asignadas..data var:.word 0,1,2,3,4.text.globl main main: li $v0,9 li $a0,8 #se pide en bytes. (dos palabras) syscall #heap por defecto en 0x10040000 move $t0,$v0 li $t1,0xffffffff sw $t1,0($t0) # escribe en las celdas asignadas. sw $t1,4($t0) li $v0,9 # se vuelve a pedir memoria li $a0,8 # pide, nuevamente, dos palabras. syscall # las asigna a continuación move $t0,$v0 li $t1,0xfefefefe #escribe otro patrón de bits. sw $t1,0($t0) sw $t1,4($t0) li $v0,10 # retorno al monitor. syscall Es responsabilidad del programador mantener alineado el espacio solicitado.

Direccionamiento y estructuras de datos 17 Índice general. CAPÍTULO 7.... 1 DIRECCIONAMIENTO Y ESTRUCTURAS DE DATOS.... 1 7.1 ARREGLOS... 1 7.1.1. Ejemplo de manipulación de arreglos... 1 En lenguaje C...1 En assembler...2 7.1.2. Arreglos de caracteres. Strings.... 3 En lenguaje C...4 En assembler...4 7.1.3. Diferencia entre manipulación de arreglos vía índice y vía puntero.... 5 7.1.3.1. Vía índices....5 7.1.3.2. Vía punteros...6 7.1.3.3. Versión dos, con manipulación vía punteros...6 7.1.4. Ordenar arreglos... 7 7.2. ESTRUCTURAS. STRUCT... 10 En lenguaje C... 11 En asembler... 11 7.3. MÁS SOBRE MANEJO DE STRINGS. (DIRECCIONAMIENTO DE BYTES)... 12 7.4 MANEJO DINÁMICO DE LA MEMORIA.... 15 ÍNDICE GENERAL.... 17 ÍNDICE DE FIGURAS... 17 Índice de figuras. FIGURA 7.1 ACCESOS VÍA PUNTEROS.... 15