CONJUNTO ORDENADO DE VARIABLES (ARRAYS)
Los arreglos ó conjuntos de datos ordenados (arrays) recolectan variables del MISMO tipo , guardandolas en forma secuencial en la memoria . La cantidad máxima de variables que pueden albergar está sólo limitada por la cantidad de memoria disponible . El tipo de las variables involucradas puede ser cualquiera de los ya vistos, con la única restricción de que todos los componentes de un array deben ser del mismo tipo .
La declaración de un array se realiza según la siguiente sintaxis :

tipo de las variables   nombre[ cantidad de elementos] ;

Por ejemplo :

int var1[10] ;   // declara las variables var[0], var[1],var[2], var[3]...var[9]

char nombre[50] ;

float numeros[200] ;

long double  cantidades[25] ;

Si tomamos el primer caso , estamos declarando un array de 10 variables enteras , cada una de ellas quedará individualizada por el subíndice que sigue al nombre del mismo es decir :

var1[0] , var1[1] , etc , hasta var1[9] .

Nótese que la CANTIDAD de elementos es 10 , pero su numeración vá de 0 a 9 , y nó de 1 a 10 . En resumen un array de N elementos tiene subíndices válidos entre 0 y
N - 1 . Cualquier otro número usado como subíndice , traerá datos de otras zonas de memoria , cuyo contenido es impredictible .
Se puede referenciar a cada elemento , en forma individual , tal como se ha hecho con las variables anteriormente , por ejemplo :

var1[5] = 40 ;

contador = var1[3] + 7 ;

if(var1[0] >>= 37)

..................

Tambien es posible utilizar como subíndice expresiones aritméticas , valores enteros retornados por funciones , etc . Así podríamos escribir :

printf(" %d " , var1[ ++i] ) ;

var1[8] = var1[ i + j ] ;

...............................

int una_funcion(void) ;

var1[0] = var1[ una_funcion() ] * 15 ;

Por supuesto los subíndices resultantes de las operaciones tienen que estar acotados a aquellos para los que el array fué declarado y ser enteros .
La inicialización de los arrays sigue las mismas reglas que vimos para los otros tipos de variables , es decir : Si se declaran como globales ( afuera del cuerpo de todas las funciones ) cada uno de sus elementos será automaticamente inicializado a cero . Si en cambio , su declaracion es local a una función , no se realiza ninguna inicialización , quedando a cargo del programa cargar los valores de inicio .
La inicialización de un array local , puede realizarse en su declaración , dando una lista de valores iniciales:

int numero[8] = { 4 , 7 , 0 , 0 , 0 , 9 , 8 , 7 } ;

Obsérvese que la lista está delimitada por llaves . Otra posibilidad , sólo válida cuando se inicializan todos los elementos del array , es escribir :

int  numero[] = { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 } ;

donde se obvia la declaración de la cantidad de elementos , ya que está implícita en la lista de valores constantes .
También se puede inicializar parcialmente un array , por ejemplo :

int numero[10] = { 1 , 1 , 1 } ;

en éste caso los tres primeros elementos del mismo valdran 1 , y los restantes cero en el caso que la declaración sea global , ó cualquier valor impredecible en el caso de que sea local .

CONJUNTO ORDENADO DE CARACTERES (STRINGS)
Los strings son simplementes arrays de caracteres , tal como los vimos hasta ahora , con el agregado de un último elemento constante : el caracter NULL ( ASCII == 0 , simbolizado por la secuencia de escape \0 ) . Este agregado permite a las funciones que procesan a los mismos , determinar facilmente la finalización de los datos .
Podemos generar un string , declarando :

char car_str[] = { 'A' , 'B' , 'C' , 'D' , 0 } ;

char car_str[] = { 'A' , 'B' , 'C' , 'D' , '\0' } ;

Ambas maneras son equivalentes. Sin embargo hay , en el lenguaje C , una forma más compacta de declararlos :

char car_str[] = "ABCD" ;

char car_str[5] = "ABCD" ;

int  texto[] =  "renglon 1  \n renglon 2  \n " ;    /* ERROR */

unsigned char  texto[] =  "renglon 1  \n renglon 2  \n " ;

Simplemente en la declaración del mismo se encierran los caracteres que lo componen entre comillas . Obsérvese que en la segunda declaración , se ha explicitado ( no es necesario ) , la cantidad de elementos que tiene el string , y és uno más que la cantidad de caracteres con que se lo inicializa , para dejar lugar al NULL . Todas éstas declaraciones agregan automáticamente el NULL como último elemento del array .
Un caso interesante es él de la tercer línea ( comentada como ERROR ) , con el fín de poder albergar al caracter "\n"20( ASCII 179 ) se intentó asignar el string a un array de enteros , Esto no es permitido por el compilador , que lo rechaza como una asignación inválida . La razón de ello se verá más adelante cuando analicemos punteros , ya que el string constante usado como rvalue es un puntero a char , y no a int . La solución mas común para este caso es , declarar el array como unsigned char , con lo que llevamos el alcance de sus elementos a 255 . Si tuvieramos el caso de tener que albergar en un string el caracter EOF ( -1 ) y al mismo tiempo caracteres con ASCII mayor que 127 ,se podría definir el array como int , pero su inicialización se tendrá que hacer obligatoriamente usando llaves , como vimos anteriormente .

Se deduce entonces , de lo antedicho que un string sigue siendo un array de caracteres , con la salvedad del agregado de un terminador , por lo que las propiedades que veremos a continuacion , se aplicaran indistintamente a ambos .

ARRAYS Y STRINGS COMO ARGUMENTOS DE FUNCIONES
Los arrays , como todos los otros tipos de variables , pueden ser pasados como argumentos a las funciones . Veamos esquematicamente como sería la sintaxis :


double funcion_1( float numeros[10] , char palabra[] ) ;       /*linea 1*/
.......................................................
main()                                                         /*linea 2*/
{
float numeros[10] = { 1.1 , 2.2 , 3.0 } ;                      /*linea 3*/
char palabra[] = " Lenguaje C " ;                              /*linea 4*/
double c ;                                                     /*linea 5*/

........................................................

c = funcion_1( numeros , palabra )                             /*linea 6*/

........................................................

}

double funcion_1( float numeros[10] , char palabra[] )         /*linea 7*/

{

........................................................

}


Es necesario analizar con mucho detenimiento , este último ejemplo . En la primer línea declaramos el prototipo de funcion_1() que recibe como argumentos dos arrays , uno de 10 elementos del tipo float , y otro de caracteres de longitud indeterminada . En el primer caso la función necesitará saber de alguna manera cual es la longitud del array numérico recibido, mientras que en el segundo , no hace falta , ya que la función puede ser construída para que , por sí misma , detecte la finalización del string por la presencia del caracter NULL . Se podría generalizar más el programa declarando :

double funcion_1( double numeros[] , int longitud_array , char palabra[] ) ;

en donde , en la variable longitud_array se enviaría la cantidad de elementos de numero[] .
En la tercer línea se declara el array numérico , inicializandose sólo los tres primeros elementos , y en la cuarta línea se declara el string .
En la séptima línea se dá la definición de la función , de acuerdo al prototipo escrito anteriormente .
Si miramos con detenimiento la sexta línea , el llamado a la función , vemos que los argumentos pasados sólo tienen el NOMBRE de ambos arrays . Esta es la diferencia más importante entre este tipo de estructura de datos y las variables simples vistas anteriormente , ya que los arrays son pasados a las funciones por DIRECCION y nó por valor .
En el lenguaje C se prefiere , para evitar el uso abusivo del stack , cuando hay que enviar a una función una larga estructura de datos , en lugar de copiar a todos ellos , cargar el stack sólo con la dirección de la posición de memoria donde está ubicado el primero de los mismos.
El nombre de un array equivale sintácticamente a la direccion del elemento cero así será :

numero  ==  dirección de numero[0]

palabra ==  direccion de palabra[0]


Esto habilita a las funciones a que puedan acceder a los arrays directamente , allí donde el programa los ha ubicado en la memoria , por lo que pueden MODIFICARLOS EN FORMA PERMANENTE aunque no hayan sido declarados como locales a la función misma ní globales al programa .
Es muy importante recordar este último concepto , a fín de evitar errores muy comunes , en los primeros intentos de programación en C .
Otra característica importante de los arrays es que , su nombre ( ó dirección del primer elemento ) es una CONSTANTE y nó una variable . El nombre de los arrays implican para el compilador el lugar de memoria donde empieza la estructura de datos por lo que , intentar cambiar su valor es tomado como un error , asI si escribieramos por ejemplo :



char titulo[] = "Primer titulo" ;

....................................

titulo = "subtitulo" ;

La primer sentencia es correcta , ya que estamos incializando al string , pero la segunda produciría un error del tipo " LVALUE REQUERIDO " , es decir que el compilador espera ver , del lado izquierdo de una expresión , a una variable y en cambio se ha encontrado con una constante titulo (ó sea la dirección de memoria donde está almacenada la P de "Primer título") . Esto al compilador le suena similar a una expresión de la clase : 124 = j y se niega rotundamente a compilarla .

ARRAYS MULTIDIMENSIONALES.
Las estructuras de datos del tipo array pueden tener más de una dimensión , es bastante común el uso de arrays "planos" ó matriciales de dos dimensiones , por ejemplo :

int matriz[ número total de filas ] [ número total de columnas ] ;

Si declaramos :

int matriz[3][4] ;

esquematicamente la disposicion "espacial" de los elementos seria:



columnas:	  0	   1	   2	   3



filas     0	[0][0]	[0][1]	[0][2]	[0][3]	matriz[0][]



          1	[1][0]	[1][1]	[1][2]	[1][3]	matriz[1][]


          2	[2][0]	[2][1]	[2][2]	[2][3]	matriz[2][]

Por supuesto , aunque menos usados , se pueden generar arrays de cualquier número de dimensiones .
Para inicializar arrays multidimensionales , se aplica una técnica muy similar a la ya vista , por ejemplo para dar valores iniciales a un array de caracteres de dos dimensiones , se escribirá :

char dia_de_la_semana[7][8] = {

                                 "lunes" , "martes" , " miercoles" ,

                                  "jueves" , "viernes" , "sábado" ,

                                  "domingo"

                               } ;

Acá el elemento [0][0] será la "l" de lunes , el [2][3] la "r" de miercoles , el [5][2] la "b" de sabado, etc. Nótese que los elementos [0][5] , [1][6] ,etc estan inicializados con el caracter NULL y demas [0][6] y [0][7], etc no han sido inicializados. Si le parece que en este párrafo se nos escapó un error , está equivocado , lo que ocurre es que se olvidó de contar los índices desde 0.
Este último ejemplo también podría verse como un array unidimensional de strings.