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