Lab_08 Arreglos N Dimensiones

33
1 UNIVERSIDAD DE EL SALVADOR FACULTAD DE INGENIERÍA Y ARQUITECTURA ESCUELA DE INGENIERÍA ELÉCTRICA INTRODUCCIÓN A LA INFORMÁTICA GUIA # 7: PUNTEROS Y ARREGLOS. Alumnos: José Edgardo Escobar Hernández EH10002 David Alexander Portillo Rodríguez PR10059 Jonathan Amílcar Mendoza Ortiz MO10019 Néstor Gabriel Vásquez López VL10008 Grupo de Laboratorio: 05 Instructor: Br. Raúl Alvarenga Ciudad Universitaria, Junio 2012.

Transcript of Lab_08 Arreglos N Dimensiones

Page 1: Lab_08 Arreglos N Dimensiones

1

UNIVERSIDAD DE EL SALVADOR

FACULTAD DE INGENIERÍA Y ARQUITECTURA

ESCUELA DE INGENIERÍA ELÉCTRICA

INTRODUCCIÓN A LA INFORMÁTICA

GUIA # 7:

PUNTEROS Y ARREGLOS.

Alumnos:

José Edgardo Escobar Hernández EH10002

David Alexander Portillo Rodríguez PR10059

Jonathan Amílcar Mendoza Ortiz MO10019

Néstor Gabriel Vásquez López VL10008

Grupo de Laboratorio: 05

Instructor: Br. Raúl Alvarenga

Ciudad Universitaria, Junio 2012.

Page 2: Lab_08 Arreglos N Dimensiones

2

ÍNDICE Pág.

Introducción……………………………………………………………………….03

Objetivos…………………………………………………………………………..04

Marco Teórico.........................................................................................................05

Asignación..............................................................................................................12

Conclusiones……………………………………………………………………..32

Bibliografía………………………………………………………………………33

Page 3: Lab_08 Arreglos N Dimensiones

3

INTRODUCCIÓN

Habiendo trabajado con matrices en una dimensión, también llamados vectores, en esta

ocasión nos centralizamos en estudiar las matrices que pueden ser de N dimensiones y con

las cuales podemos adentrarnos a un mundo de posibilidades para resolver todo tipo de

problemas con un alto número de herramientas que se podrán utilizar a la hora de declarar

programas de este tipo.

Se verán las formas que toman estos procesos y para los cuales se necesitan procedimientos

más completos y que son proporcionales al número de dimensiones que se quieran trabajar,

es decir, que el grado de dificultad para escribir un programa en dos dimensiones es menor

que la que se utiliza para uno de cinco dimensiones por decir un ejemplo.

No obstante se procurará en este laboratorio simplificar las dudas que se tengan y se

trabajarán unos programas con los cuales se demuestra lo que hemos planteado en esta

ocasión. Se verá como a medida se incrementan las dimensiones, se ve afectado hasta el

tamaño de los programas en cierto modo, y la comprensión que llevan.

A pesar de los beneficios de este tipo de datos, se sigue con la limitante que solo se puede

utilizar un único tipo de dato.

Page 4: Lab_08 Arreglos N Dimensiones

4

OBJETIVOS

General:

Utilizar arreglos de dos o más dimensiones en la solución de problemas complejos

que requieren del uso de este tipo de datos con los cuales se pueden resolver una

infinidad de estos.

Específicos:

Conocer las formas de acceder a los elementos de las matrices de N dimensiones,

tanto para extraer su información así como para modificarlos.

Analizar la estructura de las matrices y comprender que estas ocupan un lugar

especifico de memoria, conociendo la forma en que se guardan los datos y los

números que utilizan las mismas.

Observar que para recorrer los datos de las matrices se usan estructuras repetitivas

anidadas que usan tantas como dimensiones se tengan.

Page 5: Lab_08 Arreglos N Dimensiones

5

Marco Teórico

El lenguaje C permite el uso de matrices, es decir, se puede extrapolar a arrays incluso n-dimensionales (o tensores), aunque en la práctica el uso de arrays con más de 2 dimensiones no es muy común. La declaración de una matriz o array bidimensional es:

tipo variable_matriz[N][M];

Donde N y M son el número de filas y de columnas respectivamente (la dimensión de la matriz). Se ha escrito la dimensión con letras mayúsculas, ya que deben ser constantes, y al igual que con vectores se suelen definir con constantes, por ejemplo:

#define N 4 //número de filas de las matrices que voy a declarar #define M 5 //número de columnas de las matrices que voy a declarar main() { double matriz1[N][M], matriz2[N][M]; int matriz_entera[N][M]; ... }

Al igual que con vectores, las matrices se numeran empezando por el índice 0, con lo cual el elemento superior izquierdo es el [0][0] y el inferior derecho es el [N-1][M-1]. En la siguiente tabla se muestra cual sería la forma y los elementos de una matriz a[4][5], de tamaño 4×5. Véase que el primer elemento del array bidimensional es el a[0][0], el siguiente sería el a[0][1], y así, hasta llegar al elemento a[3][4].

Por otra parte, en lenguaje C las matrices se almacenan en memoria "por filas", es decir, los elementos de la fila primera (de índice 0) van consecutivos y cuando acaba el último de la primera fila empieza a almacenarse el primero de la segunda, y así sucesivamente hasta llegar a la última fila. La Figura 4.4 muestra como estaría almacenada en la memoria la matriz anterior a[4][5]. Hay que aclarar que algunos lenguajes de programación (FORTRAN) utilizan el convenio contrario, es decir, almacenan las matrices por columnas.

Distribución de una matriz bidimensional en memoria

Fila 0 1 2 3

En memoria

a[0][0]

a[0][1]

a[0][2]

a[0][3]

a[1][0]

a[1][1]

a[1][2]

a[1][3]

a[2][0]

a[2][1]

a[2][2]

a[2][3]

a[3][0]

a[3][1]

a[3][2]

a[3][3]

Aunque se puede trabajar con elementos por separado de una matriz, lo habitual es hacer operaciones matriciales con todos los elementos en conjunto. Al igual que no existen operadores vectoriales en lenguaje C, no existen tampoco operadores matriciales, de manera que tales operaciones hay que realizarlas con bucles. Como ya vimos, el anidamiento de bucles permite el recorrido completo de un array bidimensional o matriz, y tal recorrido puede hacerse por filas o por columnas. A continuación se muestra un sencillo

Page 6: Lab_08 Arreglos N Dimensiones

6

algoritmo que nos permite recorrer por filas completamente matrices (ya que el bucle interno actúa sobre los índices de las columnas); en primer lugar se valoran las matrices b y c con ciertos números y seguidamente se realiza la operación matricial suma (en matemáticas se define un operador suma matricial con el mismo símbolo '+', de forma que este algoritmo sería equivalente a la suma de matrices A=B+C).

int a[4][5], b[4][5], c[4][5], i, j; // se valoran las matrices origen A y B con ciertos números. for (i = 0 ; i < 4 ; i++) for (j = 0 ; j < 5 ; j++) { b[i][j] = i; c[i][j] = (i * j) + j; } // A continuación se realiza la suma matricial for (i = 0 ; i < 4 ; i++) for (j = 0 ; j < 5 ; j++) { a[i][j] = b[i][j] + c[i][j] ; }

Además del recorrido simple por filas o por columnas, pueden existir otros bucles anidados que mezclen recorridos por filas con recorridos con columnas. Esto ocurre, por ejemplo, en el producto matricial (en matemáticas A=B×C), ya que en tal operación es necesario que una fila de B se multiplique por una columna de C.

Hay que tener en cuenta que, por un lado, la declaración de matrices implica una reserva o consumo de memoria que puede ser importante, y por otro, que el uso posterior de las mismas en bucles anidados puede suponer un tiempo de ejecución considerable. Por ejemplo si es pretende trabajar con una matriz donde se almacena un elemento tipo double por cada píxel de la pantalla de alta resolución, esto supone declarar una matriz de dimensión [1024][768], es decir, un consumo de 8×1024×768=6,291,456 bytes, o sea, más de 6 MB. Posteriormente cada vez que se ejecute un bucle anidado para recorrer tal matriz, ¡se ejecutarán 1024×768=786,432 iteraciones!

Por último y como explicamos antes, se puede extrapolar todo lo anterior a arrays n-dimensionales. Así, el siguiente ejemplo declara un tensor de dimensión 4×5×6 e inicializa todos sus elementos a cero usando tres bucles anidados:

double tensor[4][5][6]; int i, j, k; for (i = 0 ; i < 4 ; i++) for (j = 0 ; j < 5 ; j++) for (k = 0 ; k < 6 ; k++) tensor[i][j][k] = 0;

Page 7: Lab_08 Arreglos N Dimensiones

7

La utilización de punteros en C es básica para la programación avanzada y más eficiente.

Algunos de sus usos son:

Que una función modifique o escriba en los parámetros que se le envía. Pasar a una función un vector (o matriz) completo como parámetro.

Para entender el concepto de puntero, hay que recordar un poco algunos conceptos de los lenguajes de bajo nivel; los que se usan en los procesadores (CPU). Recordemos que precisamente el lenguaje C es un lenguaje de alto nivel con algunos aspectos de bajo nivel, de ahí su potencia y popularidad. En concreto, hay que repasar el concepto de dirección y dato de memoria que estudiamos en el capítulo 2. Recordemos que, al final, tras todas las traducciones necesarias para que nuestro código C se ejecute, la CPU no va a trabajar con variables abstractas, sino con direcciones de acceso a memoria (en cada una de las cuales habrá un dato). Así la siguiente asignación:

a = b ;

se traducirá en código máquina, por algo que ejecutará algo como las dos siguientes instrucciones máquina:

CARGA REGISTRO, DIR(variable b) ESCRIBE DIR(variable a), REGISTRO

Es decir, se accede primero a memoria para leer el dato de la dirección de la variable b, que se almacena en un registro de la CPU, y después se accede a memoria para escribir el dato de tal registro en la dirección de la variable a.

Este sencillo ejemplo es extensible para todas las variables que hemos estado usando, ya sean double, vectores, matrices, cadenas, etc. Por eso, en general todas las variables no son más que direcciones de memoria, y es el compilador el que realiza el "trabajo sucio" de asignar a cada variable la dirección de memoria que considere más oportuna. Pues bien, un puntero es una variable cuyo valor es precisamente una dirección de memoria. Esa es precisamente la clave por la cual un programa escrito usando punteros suele ser más rápido (eficiente) que otro que no los usa: usar punteros, es decir, direcciones, evita muchas instrucciones que el compilador necesita introducir a veces para calcular las direcciones de las variables.

Pero, claro está, si se inventaron los compiladores para evitar el arduo trabajo de conocer o traducir las variables a sus direcciones, no tendría sentido tener que trabajar en un lenguaje de alto nivel como C con direcciones. Por eso, en realidad, las direcciones que van a contener los punteros se van a usar, pero no tienen por qué conocerse nunca. Eso sí, el puntero debe tener una dirección útil, es decir, la dirección de otra variable de memoria. Para comprender mejor este concepto, en la siguiente Figura se han representado los bytes de memoria, cuyas direcciones son la 0xF000F100, 0xF000F101, etc.(es decir, direcciones de 32 bits, como es habitual en los ordenadores actuales). Supongamos que una variable a tipo char (es decir, ocupa un byte) contiene la letra 'W', y va a ser colocada por el compilador en la dirección 0xF000F107. Por otro lado, una variable puntero p es colocada por el compilador en la dirección 0xF000F100. Si queremos que el puntero contenga la dirección de a, entonces a partir del byte de la dirección 0xF000F100 debe estar escrito el valor de la dirección de a: 0xF000F107. Este valor ocupa 4 bytes, que serán: 0xF0, 0x00, 0xF1, 0x07, los cuales se observan en la Figura

Page 8: Lab_08 Arreglos N Dimensiones

8

Mapa de memoria

Dirección de memoria

Dato en memoria

Variable abstracta

0xF000F100 0xF0

puntero p; apunta a la variable a

0xF000F101 0x00

0xF000F102 0xF1

0xF000F103 0x07

0xF000F104 irrelevante irrelevante

0xF000F105 irrelevante irrelevante

0xF000F106 irrelevante irrelevante

0xF000F107 'W' a

0xF000F108 irrelevante irrelevante

0xF000F109 irrelevante irrelevante

Se dice que el valor de un puntero "apunta" a una variable (en el ejemplo anterior, "p apunta a a", de ahí que la flecha de la figura salga de p y apunte a la variable a). También se suele expresar como que "el contenido de p es a". Notemos finalmente que el tamaño de un puntero depende del tamaño de una dirección, en nuestro caso 32 bits, pero no de lo que apunta el puntero. Es decir, si la variable puntero p apuntase a otra variable x tipo double, aunque x tendría un tamaño de 8 bytes, el puntero seguiría ocupando 4 bytes, es decir 32 bits de dirección.

La manera formal de declarar en C un puntero es la siguiente:

tipo *nombre_variable

Donde tipo es cualquier tipo válido de C y nombre_variable es el nombre de la variable puntero. El símbolo asterisco indicará al compilador que dicha variable es un puntero.

Existen dos operadores especiales unarios (sólo necesitan un operando) para el manejo de los punteros: & y *. El operador & devuelve la dirección de memoria de una variable, de ahí que se llame operador de dirección. El operador *, llamado por el contrario de "indirección", devuelve el valor de la variable a la que apunta. En el ejemplo anterior si se quiere que "p apunte a a", ha de hacerse: p=&a, es decir, la dirección de a la introducimos en p. Tras esto, si se quiere saber "el contenido de p" puede recurrirse a: *p, que nos daría el valor de a, o sea 'W'.

Sigamos viendo con otro ejemplo cual sería, intuitivamente, el funcionamiento de estos operadores. Supongamos que tenemos cuatro variables definidas, las dos primeras como variables enteras, a las cuales llamaremos a y b, y las otras, punteros a variable entera: p1 y p2. En general, es una buena costumbre para empezar a trabajar con punteros, nombrarlos empezando por la letra 'p', para distinguir claramente las variables que son punteros de las que son datos. La manera de declarar estas variables en C sería:

int a, b; int *p1, *p2;

Page 9: Lab_08 Arreglos N Dimensiones

9

O bien, todo junto:

int a, b, *p1, *p2;

Supongamos que el compilador ha ubicado las variables en memoria como muestra la Figura 4.6 (tenga en cuenta que un entero ocupa 32 bits, 4 bytes, lo mismo que un puntero, supuestas las direcciones de 32 bits). Tras la declaración de las cuatro variables, ninguna tiene un valor definido: a y b contendrán un valor indefinido y los punteros p1 y p2, no apuntarán a ningún sitio, como se muestra en esta tabla:

Mapa de memoria

Dirección de memoria Dato en memoria Variable abstracta

0xF000F100 Valor indefinido a

0xF000F104 Valor indefinido b

0xF000F108 Dirección indefinida p1

0xF000F10B Dirección indefinida p2

Para empezar a trabajar con las variables, asignamos un valor a cada una:

a = 3; p1 = &a; p2 = &b; b = (*p1) + 5;

El resultado que obtendremos es el que muestra en esta tabla:

Mapa de memoria

Dirección de memoria Dato en memoria Variable abstracta

0xF000F100 3 a

0xF000F104 8 b

0xF000F108 0xF000F100 p1

0xF000F10B 0xF000F104 p2

¿Qué ha ocurrido? Evidentemente, a contiene un 3, p1 contiene la dirección de a (0xF000F100), y p2 la de b (0xF000F104), debido a las tres primeras líneas de código. Pero la cuarta línea ha valorado la variable b con "lo que apunta p1" más 5. Como "lo que apunta p1" es a, y a vale 3, el resultado es que b tendrá un 8.

Obsérvese que, aunque en el ejemplo se muestran las direcciones para aclarar los conceptos, éstas no van a aparecer nunca en el código C, tal como dijimos antes. Por otro lado, conviene resaltar para fijar ideas que la dirección de cualquier variable es siempre constante (es decir, &a , &b son constantes), mientras que el contenido de un puntero (*p1, *p2) es variable.

Page 10: Lab_08 Arreglos N Dimensiones

10

Pero ¿y si hacemos después lo siguiente:?

(*p2) = (*p1) + 100;

Aquí hemos valorado de forma "indirecta" a la variable b, puesto que p2 apunta a b, y la expresión (*p2) a la izquierda de la asignación, conduce a asignar un valor a b. Por tanto, lo anterior es lo mismo que escribir:

b = a + 100;

Naturalmente la última línea que hemos escrito es más sencilla y comprensible que la penúltima, y por tanto, esta forma de usar no es realmente útil, pero es la mejor manera de comprender los punteros, para usarlos luego cuando realmente sea interesante. A partir de ahora evitaremos el uso de paréntesis en los accesos a los contenidos de los punteros como *p2, puesto que el operador de indirección * tiene una prioridad muy alta (ver apartado 4.2) y no se requieren ni son usuales tales paréntesis.

Aclaremos a continuación que varios punteros pueden apuntar a la misma variable, de forma que la modificación del "contenido de uno de los punteros", implicaría la modificación automática del contenido del resto de punteros (y, por supuesto, de la variable). Esta característica es la que nos permitirá poder modificar los parámetros de llamada de una función, y que esta modificación permanezca cuando salgamos de dicha función. Fijémonos en el siguiente trozo de código:

int a=130; int *p1, *p2, *p3; p1 = &a; p2 = p1; //p2=&a haría lo mismo p3 = p2; //p3=&a haría lo mismo

Las siguiente Figura 4.8 muestra la memoria, suponiendo que las variables empiezan a colocarse a partir de la dirección de memoria 0xF000F100.

Mapa de memoria

Dirección de memoria Dato en memoria Variable abstracta

0xF000F100 130 a

0xF000F104 0xF000F100 p1

0xF000F108 0xF000F100 p2

0xF000F10B 0xF000F100 p3

Por lo tanto, cualquier cambio en el valor de a, o del contenido de los punteros, implicaría un cambio en todas las variables. Así, *p3 = 50, es idéntico que a=50 o *p1=50, y en cualquier caso automáticamente tendremos que a valdrá igual que *p1, *p2 o *p3, o sea, 50.

Page 11: Lab_08 Arreglos N Dimensiones

11

Por último en este apartado hemos de explicar un "puntero especial" que siempre existe cuando se declara un vector (o matriz): se trata del propio nombre del vector. En efecto, la declaración de un vector como:

double vect[100];

implica que, mientras que cada una de las componentes vect[0], vect[1], etc. es un double, la expresión vect sería un puntero a double, que, como es evidente apunta al propio vector. En concreto la expresión vect no es una variable, sino simplemente la dirección (una constante) del vector. De esa forma, tiene sentido modificar un puntero declarado como tal pero no intentar modificar la dirección de vect:

double vect[100], d; double *pd1, *pd2; pd1 = vect; // pd1 también apunta ahora al vector pd2 = &d; // modificación del puntero pd2 vect = pd2; // error de compilación, vect es una constante

El uso del nombre de un vector como dirección o puntero constante, es vital para el paso de vectores como parámetros.

Page 12: Lab_08 Arreglos N Dimensiones

12

Asignación

1. Investigar las siguientes funciones de ANSI C:

GETS:

char *gets(char *cadena);

Esta función lee caracteres desde el stream apuntado por stream stdin, en el array apuntado

por cadena, hasta que se encuentre un final de fichero (EOF) o un carácter de línea nueva

es leído. Cualquier carácter de línea nueva es descartado, y un carácter nulo es escrito

inmediatamente después del último carácter leído en el array.

Valor de retorno:

La función gets retorna cadena si es realizada con éxito. Si un final de fichero (EOF) es

encontrado y ningún carácter ha sido leído en el array, entonces el contenido del array

permanece invariable y un puntero nulo es retornado. Si ocurre un error de lectura durante

el proceso, el contenido del array es indeterminado y un puntero nulo es retornado.

Ejemplo:

#include <stdio.h>

int main()

{

char oracion[81];

printf( "Escribe una oracion:\n");

printf( "\nHas escrito: \"%s\"\n", gets(oracion) );

return 0;

}

Page 13: Lab_08 Arreglos N Dimensiones

13

RANDOM:

int rand(void);

La función rand calcula una secuencia de números enteros pseudo-aleatorios en el intervalo

de 0 á RAND_MAX.

Valor de retorno

La función rand retorna un entero pseudo-aleatorio.

Ejemplo:

#include <stdio.h>

#include <stdlib.h>

int main(){

unsigned int i=1;

printf( "30 numeros generados aleatoriamente: \n\n" );

for( i=1; i<30; i++ )

printf( "%d, ", rand() );

printf( "%d\n", rand() );

return 0;

}

Page 14: Lab_08 Arreglos N Dimensiones

14

2. Escribir un programa que multiplique dos matrices:

//PROGRAMA QUE MULTIPLICA DOS MATRICES

#include <stdlib.h>

#include <stdio.h>

#define SIZE 100

int main(int main, char *argv[])

{

float matriz1[SIZE][SIZE], matriz2[SIZE][SIZE];

float solucion[SIZE][SIZE], suma;

int filas1,filas2, col1, col2, i, j, k;

printf("\nEste programa realiza el producto de dos

matrices.\n");

printf("ADVERTENCIA: las matrices no pueden tener dimensiones

de filas o columnas mayores que 100.\n");

do{

printf("\nIntroduzca la dimension de las

matrices:\n");

printf("\nfilas matriz1: ");

scanf("%d", &filas1);

printf("columnas matriz1: ");

scanf("%d", &col1);

printf("\nfilas matriz2: ");

scanf("%d", &filas2);

printf("columnas matriz1: ");

scanf("%d", &col2);

if ( (filas1 >100 || col2 > 100) || (filas2 > 100 ||

col2 > 100) )

printf("\nLa matrices no pueden tener

dimensiones de filas y columnas mayores q 100.\n\n");

}while( (filas1 >100 || col2 > 100) || (filas2 > 100 || col2 >

100) );

if ( col1 != filas2)

{

printf("\n\nEl numero de columnas de matriz 1");

printf(" debe ser igual al numero de filas de matriz

2\n\n");

}

if (col1 == filas2)

{

printf("\nIntroduzca los elementos de la primera

matriz:\n");

for (i = 0; i < filas1; i++) {

for (j = 0; j < col1; j++) {

printf("a(%d,%d): ", i + 1, j + 1);

scanf("%f", &matriz1[i][j] );

}

}

printf("\n");

printf("\nIntroduzca los elementos de la segunda

matriz:\n");

Page 15: Lab_08 Arreglos N Dimensiones

15

for (i = 0; i < filas2; i++)

{

for (j = 0; j < col2; j++)

{

printf("b(%d,%d): ", i + 1, j + 1);

scanf("%f", &matriz2[i][j] );

}

}

printf("\n");

for (i = 0; i < col1; i++)

{

for (j = 0; j < col2; j++)

{

suma = 0.0;

for (k = 0; k < col1; k++)

suma += matriz1[i][k] *

matriz2[k][j];

solucion[i][j] = suma;

}

}

printf("\nSolucion:\n\n\n");

for (i = 0; i < filas1; i++)

{

for (j = 0; j < col2; j++)

printf("%8.2f ", solucion[i][j]);

printf("\n");

}

}

printf("\n");

system("pause");

return 0;

}

Page 16: Lab_08 Arreglos N Dimensiones

16

Page 17: Lab_08 Arreglos N Dimensiones

17

3. Hacer los cambios necesarios al código anterior para que también se pueda

encontrar la matriz inversa del producto efectuado.

#include <stdlib.h>

#include <stdio.h>

#define col 100

#define fil 100

int main(){

int filaA, columnaA, filaB, fA, cB, columnaB, i, j, k;

float mat[fil][col];

float matb[fil][col];

float matc[fil][col];

float mati[fil][col];

printf("\nINGRESE DIMENSIONES DE LAS MATRICES, FILAS POR

COLUMNAS, PRIMERO DE A Y LUEGO DE B\n");

printf("\n");

//RESTRICCIONES MATRICES A INGRESAR, NUMEROS NO NEGATIVOS Y

MENORES QUE 100, COLUMNA A IGUAL A FILA MATRIZ B

do{

do{

printf("Numero de filas Matriz A: "); scanf("%d", &filaA);

printf("Numero de columnas Matriz A: "); scanf("%d", &columnaA);

printf("Numero de filas Matriz B: "); scanf("%d", &filaB);

printf("Numero de columnas Matriz B: "); scanf("%d", &columnaB);

if ((filaA <= 0 || filaA >100) || (columnaA <= 0 || columnaA >=

100) || (filaB<= 0 || filaB >100) || (columnaB <= 0 || columnaB

>= 100) )

printf("\nNUMERO MAXIMO PARA FILAS Y/O COLUMNAS: %d\n", fil);

}while ((filaA <= 0 || filaA >=100) || (columnaA <= 0 ||

columnaA >= 100 )||(filaB<= 0 || filaB >100)||(columnaB <= 0 ||

columnaB >= 100));

if (columnaA != filaB)

printf("¡¡¡EL NUMERO DE COLUMNAS DE LA MATRIZ A TIENE QUE SER

IGUAL AL NUMERO DE COLUMNAS DE LA MATRIZ B!!! \n");

}while (columnaA != filaB);

// LEER MATRIZ A

printf("Ingrese matriz A\n");

for (i = 0; i < filaA; i++)

for ( j = 0; j < columnaA ; j++)

{

printf("Elemento[%d, %d] = ", i+1, j+1); scanf("%f",

&mat[i][j]);

}

printf("\n");

//LEER MATRIZ B

Page 18: Lab_08 Arreglos N Dimensiones

18

printf("Ingrese matriz B\n");

for (i = 0; i < filaB; i++)

for ( j = 0; j < columnaB ; j++)

{

printf("Elemento[%d, %d] = ", i+1, j+1);

scanf("%f", &matb[i][j]);

}

printf("\nLA MATRIZ MULTIPLICACIÓN ES: \n");

//MULTIPLICAR MATRIZ A Y B

for ( i = 0; i < filaA; i++)

{

for (j = 0; j < columnaB; j++)

for(k = 0; k < columnaA; k++)

{

matc[i][j]=(matc[i][j] +(mat[i][k]*matb[k][j]));

}}

//MOSTRAR RESULTADO DE MULTILPLICACION EN MATRIZ C

for ( i = 0; i < filaA; i++)

{

for (j = 0; j < columnaB; j++)

printf("%.2f\t", matc[i][j]);

printf("\n");

}

//MATRIZ INVERSA

filaA = columnaB;

filaB = columnaA;

for (i= 0; i < filaA; i++)

for (j = 0; j < columnaB; j++)

{

mati[j][i] = matc[i][j];

}

printf("\nLA MATRIZ INVERSA DE LA MATRIZ MULTIPLICACION ES:\n");

//IMPRIMIR INVERSA

for (i = 0; i < filaA; i++)

{

for (j = 0; j < columnaA; j++)

printf("%.2f\t", mati[i][j]);

printf("\n");

}

system("pause");

return 0;

}

Page 19: Lab_08 Arreglos N Dimensiones

19

Page 20: Lab_08 Arreglos N Dimensiones

20

4. Si una matriz se declara como double A [ 4 ][ 5 ], responder las siguientes

preguntas:

¿Cuántos Bytes utiliza?

Ya que un dato tipo double ocupa 8 bytes de memoria y son 20 elementos de la matriz,

entonces se utilizan 8 x 20 = 160 Bytes.

¿Cómo se inicializa?

Double [ ] [ ] {

{elemento, elementoN}

{elemento, elementoN}

}

Se puede inicializar con los valores, de la forma mostrada anteriormente, o también desde

línea de comando. También otra alternativa es ingresarlos con dos ciclos repetitivos, con

dos for, por ejemplo, uno que lleve la cuenta de las filas y otras que lleve el número de

columnas.

Si la dirección de memoria del elemento A[ 2 ][ 3 ] es 0XFEABACA

1. ¿Cuál es la dirección del elemento A[ 0 ][ 0 ]?

Nos auxiliaremos de la siguiente tabla para ubicarnos en los espacios de memoria con los

que cuenta la matriz, esta tabla presenta las posiciones:

0 1 2 3 4

0 A[0][0] A[0][1] A[0][2] A[0][3] A[0][4]

1 A[1][0] A[1][1] A[1][2] A[1][3] A[1][4]

2 A[2][0] A[2][1] A[2][2] A[2][3] A[2][4]

3 A[3][0] A[3][1] A[3][2] A[3][3] A[3][4]

Debido a que las ubicaciones de memoria se cuentan en bytes, y se van llenando primero

las filas y luego las columnas, entonces podemos hacer una tabla que represente las

posiciones de memoria partiendo de la que nos da el problema.

Lo que se hace en el primer caso es que se multiplica el número de espacios que hay antes

de 0xFEABACA por 8 bytes que ocupa cada uno, son 13 espacios antes por 8 bytes cada

uno por ser double, esto hace un total de 104 bytes, convertido a hexadecimal este número

es: 0x68

Page 21: Lab_08 Arreglos N Dimensiones

21

Entonces la posición de la memoria es:

0xFEABACA – 0x68 = 0xFEABA62

2. ¿Cuál es la dirección de A[ 3 ][ 4 ]?

Ahora lo que se hace es sumar a la posición que nos dan los espacios que hay desde ella

hasta el elemento que se nos pide, son 6 espacios por 8 = 48 (0x30) Bytes de memoria,

entonces la posición es:

0xFEABACA + 0x30 = 0xFEABAFA

Así se indica en la siguiente representación de matriz:

0 1 2 3 4

0 0xFEABA62

1

2 0xFEABACA

3 0xFEABAFA

Mostrar todos las formas posibles de acceder a los elementos de A

- La forma más fácil es indicar la manera general de ingresar, que es poniendo

el nombre de la matriz y entre corchetes el elemento al cual se quiere

ingresar:

A[ 2 ][ 4 ] = 20

Con este ejemplo modificamos el valor que tenía la posición por 20.

- También se puede hacer por medio de un puntero:

*(b + fila) + columna, donde no se necesita que elemento se llame por su posición, sino

que el puntero mismo se mueve por todas las direcciones y devuelve el valor que está en

ella.

- Si se quiere recorrer elemento por elemento se hace con dos ciclos anidados

los cuales ingresan a las posiciones primero en fila y luego por columnas:

for (f = 0; f < filas; f++ )

for (c = 0; f < columnas; c++){

sentencias

}

Page 22: Lab_08 Arreglos N Dimensiones

22

Mostrar todas las formas para trabajar con las direcciones de los elementos

de A

- Se puede efectuar con punteros:

int *punt;

int matriz[ 4 ];

printf(“La dirección del elemento es %p”, &matriz[ 3 ]);

¿Cuáles son los índices de inicio y final de la matriz A?

A [0] [0] para primer elemento.

A [3] [4] para el último elemento.

¿Cuántos y cuáles punteros están en juego?

Si no se declaran más punteros que la misma matriz sólo existe ese, es decir, el mismo

puntero es una matriz y sólo existe ese.

Hacer un dibujo que incluya todos los punteros que están presentes, incluir

también la matriz.

A[0][0] A[0][1] A[0][2] A[0][3] A[0][4]

A[1][0] A[1][1] A[1][2] A[1][3] A[1][4]

A[2][0] A[2][1] A[2][2] A[2][3] A[2][4]

A[3][0] A[3][1] A[3][2] A[3][3] A[3][4]

El puntero declarado cambiará su dirección dependiendo del ciclo a usar, y se moverá por

todos los elementos.

*(punt + fila) + columna = 0XFEABA62

Page 23: Lab_08 Arreglos N Dimensiones

23

5. Hacer un programa en ANSI C que pueda indicar si una matriz es identidad

o no.

#include <stdlib.h>

#include<stdio.h>

#define SIZE 100

int main (int argc, char *argv[])

{

int matrizA[SIZE][SIZE];

int matrizB[SIZE][SIZE];

int af1, filas, col, i, j;

printf("\nEste programa identifica una matriz

identidad.\n");

printf("ADVERTENCIA: La matriz no puede tene dimensiones

de filas y columnas mayores q 100.\n");

do

{

printf("\nIntroduzca la dimension de las

matrices:\n");

printf("\nfilas matriz: ");

scanf("%d", &filas);

printf("columnas matriz: ");

scanf("%d", &col);

if( (filas >100 || col > 100) )

printf("\nLa matriz no puede tener

dimensiones de filas y columnas mayores q 100.\n\n");

}while( (filas >100 || col > 100) );

if ( filas != col )

printf("\n\nEl numero de filas debe ser igual al

numero de columnas\n\n\n");

if ( filas == col )

{

printf("\nIntroduzca los elementos de la

matriz:\n");

for (i = 0; i < filas; i++) {

for (j = 0; j < col; j++) {

printf("a(%d,%d): ", i + 1, j +

1);

scanf("%d", &matrizA[i][j] );

}

}

for (i = 0; i < filas; i++)

{

for (j = 0; j < col; j++)

matrizB[i][j] = 0;

}

Page 24: Lab_08 Arreglos N Dimensiones

24

for (i = 0; i < filas; i++)

{

for (j = 0; j < col; j++)

{

if ( i == j)

matrizB[i][j] = 1;

}

}

for (i = 0; i < filas; i++)

{

for (j = 0; j < col; j++)

{

if ( matrizA[i][j] ==

matrizB[i][j])

af1 = 1;

else

af1 = 0;

}

}

if ( af1 == 1)

{

printf("\nLA MATRIZ ES IDENTIDAD\n.");

printf("\nLa matriz introducida

es:\n\n");

for (i = 0; i < filas; i++)

{

for (j = 0; j < col; j++)

printf("%d\t",

matrizA[i][j]);

printf("\n\n");

}

}

else

{

printf("\nLA MATRIZ NO ES

IDENTIDAD\n.");

printf("\nLa matriz introducida

es:\n\n");

for (i = 0; i < filas; i++)

{

for (j = 0; j < col; j++)

printf("%d ",

matrizA[i][j]);

printf("\n\n");

}

printf("\nPara ser matriz identidad debe

ser de la siguiente manera:\n\n");

for (i = 0; i < filas; i++)

{

for (j = 0; j < col; j++)

Page 25: Lab_08 Arreglos N Dimensiones

25

printf("%d ",

matrizB[i][j]);

printf("\n\n");

}

}

}

system("pause");

return 0;

}

Page 26: Lab_08 Arreglos N Dimensiones

26

6. Hacer un programa en ANSI C que pueda resolver un sistema de ecuaciones

por el método de Gauss Jordan.

#include <stdlib.h>

#include <stdio.h>

#include <math.h>

#define EPS 1.e-12

void main(void)

{

int i, j, n, vRet;

double **a, *x, *b;

double **crearMatriz(int m, int n);

int triang(int n, double **a, double *b);

int pivot(int n, int k, double **a);

void sust(int n, double **a, double *b, double *x);

void escribir(int n, double *x);

/* Lectura de datos y creaci¢n dinamica de matrices y

vectores */

printf("\nNumero de ecuaciones: ");

scanf("%d", &n);

a=crearMatriz(n, n);

printf("\nMatriz del sistema:\n");

for (i=0; i<n; i++)

for(j=0; j<n; j++)

{

printf("a(%d, %d): ", i+1, j+1);

scanf(" %lf", &a[i][j]);

}

b=calloc(n, sizeof(double));

x=calloc(n, sizeof(double));

printf("\nTermino independiente:\n");

for (i=0; i<n; i++)

{

printf("b(%d): ", i+1);

scanf(" %lf", &b[i]);

}

/* Triangularizacion de la matriz */

vRet=triang(n, a, b);

if (vRet!=1)

{

printf("Ha aparecido un pivot nulo o muy

pequeño. Agur! \n");

exit(1);

}

/* Vuelta atras */

sust(n, a, b, x);

/* Escritura de resultados */

escribir(n, x);

system("pause");

} /* Fin de main() */

Page 27: Lab_08 Arreglos N Dimensiones

27

/* Funcion para hallar el pivot de la columna k */

int pivot(int n, int k, double **a)

{

int i, imaximo;

double maximo=0.0;

double va(double);

for (i=k; i<n; i++)

{

if (maximo<va(a[i][k]))

{

maximo=va(a[i][k]);

imaximo=i;

}

}

return imaximo;

}

/* Funcion para realizar la triangularizacion de una matriz */

int triang(int nec, double **a, double *b)

{

int i, j, k, ipivot, error;

double fac, *temp;

double va( double );

for (k=0; k<nec-1; k++)

{

ipivot=pivot(nec, k, a);

/* intercambio de los punteros a las filas k e

imaximo */

temp=a[k];

a[k]=a[ipivot];

a[ipivot]=temp;

fac=b[k];

b[k]=b[ipivot];

b[ipivot]=fac;

for (i=k+1; i<nec; i++)

{

if (va(a[k][k]) < EPS)

return error=-1;

fac=-a[i][k]/a[k][k];

for (j=k; j<nec; j++)

a[i][j]+=a[k][j]*fac;

b[i]+=b[k]*fac;

}

}

return error=1;

}

void sust(int n, double **a, double *b, double *sol)

{

int i, k;

double sum;

for (k=n-1; k>=0; k--)

{

sum=0.0;

for (i=k+1; i<n; i++)

sum+=a[k][i]*sol[i];

Page 28: Lab_08 Arreglos N Dimensiones

28

sol[k]=(b[k]-sum)/a[k][k];

}

}

double **crearMatriz(int m, int n)

{

double **matriz, *mat;

int i;

matriz=calloc(m, sizeof(double *));

matriz[0]=mat=calloc(m*n, sizeof(double));

for(i=1; i<m; i++)

matriz[i]=mat+n*i;

return matriz;

}

void escribir(int n, double *solucion)

{

int i;

printf("\nEl vector solucion es: \n");

for (i=0; i<n; i++)

printf("solucion(%d)= %.4lf\n", i, solucion[i]);

printf("\n");

}

double va(double u)

{

if (u<0.0)

return -u;

else

return u;

}

Page 29: Lab_08 Arreglos N Dimensiones

29

Page 30: Lab_08 Arreglos N Dimensiones

30

7. En una matriz de n x n escribir las tablas correspondientes, por ejemplo para

n = 4:

El último dato es 16 ya que nxn = 16. Considerar un máximo de n = 20 y este

dato (el valor de n) debe ser introducido desde la línea de comandos, se

recomienda utilizar la función atoi( ) que se encuentra en stdlib.h

#include <stdio.h>

#include <stdlib.h>

#define SIZE 20

int main(int main, char *argv[])

{

int matriz[SIZE][SIZE];

int i, j, n, k = 1;

n = atoi(argv[1]);

if ( n > 20)

{

printf("ADVERTENCIA: ");

printf("las matrices no pueden tener dimensiones

de filas o columnas mayores q 20.\n\n");

return 0;

}

for (i = 0; i < n; i++)

{

for (j = 0; j < n; j++)

{

matriz[i][j] = k * (j +1);

}

k++;

}

printf("\nTablas:\n\n");

for (i = 0; i < n; i++)

{

for (j = 0; j < n; j++)

printf("%3d ", matriz[i][j]);

printf("\n\n");

}

printf("\n");

system("pause");

return 0;

}

Page 31: Lab_08 Arreglos N Dimensiones

31

Page 32: Lab_08 Arreglos N Dimensiones

32

CONCLUSIONES

Con el presente trabajo hemos desarrollado los conceptos investigados y los usos

de Arreglos de n-dimensiones implementando el manejo de cadenas de caracteres,

el uso de punteros; con los cuales ya no solo le demos un tamaño de memoria sino

que además podemos ahora evaluar a través de un puntero y asignar las variables

que deseamos y el tipo, y cantidad de memoria a asignarle.

Se ha observado que para ingresar a los datos alojados en las matrices o arreglos

que estamos trabajando se pueden hacer de muchas formas, desde una declaración

simple hasta el uso de ciclos repetitivos utilizando punteros. No hay una única

solución para implementarlo, sino que se las maneras de procesar los datos son

diversas y estos nos ayudan a optimizar los programas y a recortar los recursos a

utilizar en la memoria.

Cada dato almacenado está ocupando una dirección específica de memoria con la

cual se identifica el dato, y se debe tener cuidado que el puntero contiene la

dirección de memoria, no su contenido, por ello el programa se hace más ágil y

mejor optimizado en cuanto a recursos.

Page 33: Lab_08 Arreglos N Dimensiones

33

BIBLIOGRAFÍA

Luis Joyanes Aguilar. Programación en C

Guías de Laboratorio IIE-115