Introducción al C bajo UNIX

Texto completo

(1)

Hernando Silva Varela

Escuela Universitaria de Informática Universidad de Valladolid campus Segovia

hernando@infor.uva.es

(2)

Introducción al C bajo UNIX Compilación de un programa

Tema 1: Compilación de un programa

Figura 1: Etapas de compilación de un programa en C

Orden de compilación (en línea): cc chero fuente.c

(3)

Etapas de compilación:

Preproceso Es el proceso en el cual se interpretan y ex-panden directivas, se reemplazan constantes, se eliminan comentarios, etc. Por ejemplo, para ver la salida "pre-procesada"en un chero se puede usar

cc -E chero fuente.c >salida.txt

Compilación Estas etapa convierte un chero fuente en C en un chero objeto en lenguaje máquina. Para preprocesar y compilar un chero, generando un chero objeto sin generar ejecutable, se puede usar la orden

cc -c chero fuente.c

Enlace En esta etapa se enlazan los cheros objeto del usuario con los cheros objeto del sistema con el n de generar el chero ejecutable nal. Para enlazar un chero objeto y generar un chero ejecutable se puede usar

cc chero fuente.o -o chero ejecutable

(4)

Introducción al C bajo UNIX Generalidades del lenguaje C

Generalidades del lenguaje C

El C es un lenguaje de programación de propósito general.

C es un lenguaje estructurado que permite utilizar las estructuras básicas: secuencia, selección e iteración.

C es un lenguaje de nivel medio que permite acceder a recursos de bajo nivel en el sistema al combinar las características de los lenguajes ensamblador y los lenguajes de alto nivel.

El lenguaje C es fácilmente transportable que se puede compilar en casi cualquier máquina con mínimas o nulas modicaciones.

(5)

Estructura de un programa en C

La estructura de un programa en lenguaje C está formada por los siguientes elementos:

Bloque de declaración de cheros de cabecera y otras directivas. Debe declararse un chero de cabecera por cada biblioteca a utilizar.

Bloque de declaración de variables globales y denición de funciones del programa.

La función main() de aparición obligatoria.

EJEMPLO:

/* Bloque de declaración de ficheros de cabecera y otras directivas */ #include <stdio.h>

/* Bloque de declaración de variables globales y definición de funciones del programa */

int a;

/* Función main */ main() {

(6)

Introducción al C bajo UNIX Reglas generales de sintaxis en C

Identicadores

Un identicador sirve para identicar a todas las variables, constantes y funciones. Los identicadores deben cumplir las siguientes normas:

a) Pueden formarse con cualquier combinación de letras y números pero deben empezar por letra.

b) Se puede utilizar también el carácter

c) Es conveniente evitar el uso de ñ, Ñ y tildes.

d) El lenguaje C es sensible a mayúsculas y minúsculas.

e) El identicador no debe coincidir con ninguna palabra reservada del C como if, for, break, to, while, etc

Comentarios

a) Los comentarios en C empiezan por /* y terminan con */, pueden ser de varias líneas y se pueden poner casi en cualquier parte del programa.

(7)

Punto y coma

El punto y coma (;) se coloca al nal de cada sentencia simple tal como declaración de variable, asignación de una variable y llamada a una función.

Funciones del sistema y del usuario

a) En C existen dos tipos de funciones: las que propor-ciona el sistema y las que dene el programador.

b) Las funciones del sistema están declaradas en los cheros de cabecera <*.h>que se indican al principio del programa.

c) Si el programador requiere funciones no denidas en el sistema, deberá generarlas.

d) Dentro de cada función se distinguen dos partes se-cuenciales: la declaración de variables y el código.

e) La ejecución empieza y termina en la función main

f) La ejecución de un programa en C termina cuando:

- Se llega al nal de main

- Se ejecuta una sentencia exit()

(8)

Introducción al C bajo UNIX Reglas generales de sintaxis en C

EJEMPLO:

/* Bloque de declaración de ficheros de cabecera y otras directivas */ #include <stdio.h>

#include <stdlib.h>

/* Bloque de declaración de variables globales y definición de funciones del programa */

float PI=3.141592;

int promedio(int a,int b) {

return(a + b)/2; }

/* Función main */ main()

{

int i,j; int media;

/* Este es un comentario */ i=7;

j = 9;

media=promedio(i,j);

printf("La media de %d y %d es %d\n", i, j, media); exit(0);

(9)

Variables escalares elementales

int Las variables de tipo int almacenan datos de tipo entero con signo y tienen, generalmente, un tamaño de 2 octetos. Las variables de este tipo se pueden declarar así:

int suma;

int suma, resta, a, b, c; int suma=5;

char El tipo char sirve ara almacenar datos de tipo caracter y es similar al entero int, sin embargo, solo almacena datos de 8 bits en el intervalo [-128,127]. La declaración de este tipo de variables se realiza de manera similar al tipo int.

Aunque una variable sea de tipo char, se le puede ma-nipular como un número y los siguientes códigos de programa son perfectamente válidos.

#include <stdio.h> main() {

char letra;

letra='A';

printf("La variable letra vale %c.",letra); }

(10)

Introducción al C bajo UNIX Variables escalares elementales

#include <stdio.h> main() {

char letra; int num;

letra='A'; num=65;

printf("letra vale %c y num vale %i.",letra,num); }

SALIDA: letra vale A y num vale 65

#include <stdio.h> main() {

char c;

c=65;

printf("La variable c vale %i.",c);

printf("y almacena el caracter ASCII %c.",c); }

SALIDA: La variable c vale 65 y almacena el caracter ASCII A.

(11)

Estructuras básicas de control

Estructura secuencial

La estructura secuencial en C es bastante simple y con-siste únicamente en colocar una sentencia a continuación de la otra. Por supuesto, cada sentencia debe terminar en (;). Considérese el siguiente código:

a=19; b=5;

printf("%i mas %i es %i, ", a, b, a+b); printf("\ny %i menos %i, %i.",a, b, a-b);

Estructura alternativa if ... else ...

La estructura if ... else se usa para ejecutar código de manera selectiva y tiene la siguiente sintaxis:

if (expresión) sentencia_1; else

sentencia_2;

(12)

Introducción al C bajo UNIX Estructuras básicas de control

Ejemplos con if ... else ...

#include <stdio.h>

int a=4; int b=0;

main() {

if (a>b) printf("%i es mayor que %i.", a, b); else printf("%i es mayor que %i.", b, a);

}

#include <stdio.h>

int a=4; int b=0;

int mayor,menor;

main() {

if (a>b) {

mayor=a; menor=b; }

else {

mayor=b; menor=a; }

(13)

Operadores lógicos y relacionales

Los operadores relacionales más comunes en C son: < menor que, <= menor o igual que,

> mayor que, >= mayor o igual que, == igual que, != diferente que

Los operadores lógicos más comunes son: && AND, || OR, ! NO

Por ejemplo, el pseudo código

si a=5 o b=3

Escribir "La condición se cumple" n si

se codica en C como if ((a==5)||(b==3))

(14)

Introducción al C bajo UNIX Estructuras básicas de control

La estructura alternativa switch y la

instrucción break

La sentencia switch es una estructura decisión múltiple que verica si una expresión coincide con una o varias etique-tas formadas por valores constantes enteros transriendo el control a la etiqueta coincidente. La sintaxis general es: switch(expresión) {

case valor1: sentencia 1; case valor2: sentencia 2; ...

default: sentencia N; }

En el caso de que 'expresión' tenga el 'valor 1', se ejecutarán la 'sentencia 1' y todas las que le siguen; si expresión tiene 'valor 2', se ejecutarán la 'sentencia 2' y todas las que le siguen, y así hasta la 'sentencia N-1'.

Si 'expresión' no coincide con ninguna de las etiquetas, el control se transere a la sentencia etiquetada como 'default'; la etiqueta 'default', sin embargo, es opcional.

(15)

Ejemplo con la sentencia 'switch'

#include <stdio.h>

int a=3;

main(){

switch (a) {

case 1: printf("La variable a vale 1"); break;

case 2: printf("La variable a vale 2"); break;

case 3: printf(" 3 "); case 4: printf(" 4 "); case 5: printf(" 5 ");

break;

default: printf("a no está entre 1 y 5"); }

}

En este caso, si a vale 3 se ejecutarán todas la sentencias entre el case 3 y el siguiente break.

3 4 5

En cambio, si a no está entre 1 y 5, se ejecutará la sen-tencia denida por omisión.

(16)

Introducción al C bajo UNIX Estructuras básicas de control

Estructura iterativa do ... while

La sintaxis de la estructura iterativa do ... while es:

do

instrucción; while (expresión);

- El cuerpo del bucle se ejecuta al menos una vez. - Hay un punto y coma (;) después de la expresión.

- Para ejecutar varias instrucciones se encierran entre llaves.

#include <stdio.h> main() {

int a,b; char resp;

do {/* Lectura de datos */

printf("\n Primer número: "); scanf("%i", &a);

printf("\n Segundo número: "); scanf("%i", &b);

/* Mostrar los datos */

printf("\n\n Suma: %i", a+b); printf("\n Resta: %i", a-b); printf("\n Producto: %i", a*b);

/* ¾Desea continuar? */

printf("\n ¾Desea continuar (S/N)?"); resp=getchar();

(17)

Estructura iterativa while

La estructura iterativa while funciona de manera similar a la estructura do ... while y su sintaxis es la siguiente: while (expresión)

instrucción;

- Se evalúa primero la expresión y luego se ejecuta el cuerpo - Los cuerpos de más de una instrucción van entre llaves

Estructura iterativa for

La estructura iterativa for tiene la siguiente sintaxis: for (expresión1; expresión2; expresión3)

sentencia;

- Normalmente expresión1 y expresión3 son asignaciones Ejemplo: for (i=0;i<n;i=i+1)

- La expresión2 es una expresión relacional

- Cualquiera de las tres expresiones se puede omitir Ejemplo: for( ; ; ) { ... }

- Se pueden colocar más expresiones separadas por comas Ejemplo: for (i=0,j=1; (i!=0)||(j<10); j=j+1)

(18)

Introducción al C bajo UNIX Estructuras básicas de control

Ejemplo con la estructura iterativa for

#include <stdio.h>

/* Imprime la tabla Fahrenheit-Celsius */ main ()

{

int fahr;

for (fahr = 0; fahr <= 300; fahr = fahr + 20)

printf("%4d %6.1f\n", fahr, (5.0/9.0)*(fahr-32)); }

break y continue

La sentencia 'break' provoca una salida anticipada de un 'for', 'while' o 'do ... while', tal como lo hace en 'switch'. Para ello provoca la nalización del bucle más interno en el que está contenida.

La sentencia 'continue', por su parte, provoca que se inicie la siguiente iteración del ciclo 'for', 'while' o 'do ... while' que la contiene.

(19)

Tipos de datos en C

Tipos de datos escalares

El C admite cuatro tipos de datos escalares, que son los que se presentan en la siguiente tabla:

Tipo Descripción básica

char Carácter y entero en un byte (octeto)

int Entero

oat Número en coma otante de precisión normal

double Número en coma otante de doble precisión a) Tipos de datos enteros

Como ya se mencionó, los tipos de datos enteros en C son char e int. Además, a los datos de tipo int se les puede asignar los calicadores short, long, signed y unsigned; los dos últimos calicadores son aplicables al tipo char.

Las declaraciones típicas para estos tipos de datos pueden ser de las siguientes formas:

short int x; /* Entero corto [−215,215 − 1] */

long int y; /* Entero largo [−231,231 − 1] */

unsigned int z; /* Entero sin signo [0,216 − 1]*/

char u; /* Carácter [0,27] */

signed char a; /* Carácter con signo [−27,27 − 1]*/

(20)

Introducción al C bajo UNIX Tipos de datos en C

b) Tipos de datos reales

Existen en C dos formatos para almacenar datos de tipo real: float y double. Estos formatos se distinguen entre sí en que el segundo utiliza más bits que el primero para el almacenamiento.

Existe además un calicador que amplía la resolución (y el tamaño) de la representación: long. Las declaraciones típicas para estos datos serían de la forma:

oat x; /* Tipo real de cuatro bytes */ double y; /* Tipo real de 8 bytes */ long double /* Tipo real de 12 bytes */

#include <stdio.h> #define PI 3.1416

main() {

float radio, area;

printf("\ Introduzca el radio: "); scanf("%f", &radio);

superficie=PI*radio*radio;

(21)

Tipos de datos compuestos

En C, los tipos de datos compuestos se componen de tipos de datos más sencillos; normalmente escalares. Se tienen tres tipos de datos compuestos en C: vector, struct y union.

a) El tipo vector (array)

El tipo vector (array) es una estructura formada por un bloque de N objetos numerados por su índice que va desde 0 hasta N-1. La sintaxis declarativa es:

<tipo>nombre [<tamaño>];

Por ejemplo, int v[10]; declara una variable v de

diez elementos que se referencían como v[0], ..., v[9]

NOTAS: 1) La declaración de un chero no conlleva necesariamente su inicialización y puede ser necesario ini-cializar explícitamente. 2) En C no se puede asignar

direc-tamente un vector a otro. Por ejemplo, dados float

a[100],b[100]; no se puede hacer la operación a=b.

Los arreglos multidimensionales se declaran de acuerdo a la siguiente sintaxis:

<tipo>nombre [<tam1>][<tam2>] ... [<tamN>]

(22)

Introducción al C bajo UNIX Tipos de datos en C

El tipo registro: struct

El tipo registro (struct) es un tipo de dato formado por una serie de elementos llamados campos que pueden ser de diferente tipo. Una de las sintaxis para el tipo struct es

struct etiqueta { tipo1 campo1;

tipo2 campo2; ...

tipoN campoN; };

la cual dene un nuevo tipo de variable (etiqueta) que tiene la estructura dada y se puede utilizar para declarar variables usando la sintaxis:

struct etiqueta var1, var2, ..., varN;

También se pueden declarar las variables al mismo tiem-po que se declara la estructura usando la sintaxis:

struct etiqueta { tipo1 campo1;

tipo2 campo2; ...

tipoN campoN;

(23)

Para hacer referencia a alguno de los elementos de una variable de tipo estructura se usa la sintaxis se adjunta al nombre de la variable el del campo, separados por un punto.

Por ejemplo, dada la declaración

struct punto { int x;

int y;

} a, b, c;

se pueden ejecutar las siguientes sentencias:

a.x=1; b.y=2;

Las variables de tipo estructura se pueden inicializar al declararse. Por ejemplo

struct punto d={10,6};

y se pueden copiar todos los campos de una estructura a otra, siempre y cuando sean del mismo tipo. Es decir, dados los ejemplos anteriores, la sentencia

a=b;

(24)

Introducción al C bajo UNIX Tipos de datos en C

c) El tipo union

El tipo union es una estructura de datos que puede contener, en distintos momentos, datos de tamaños y tipos distintos. Las uniones se usan para guardar diferentes vari-ables en la misma zona de memoria.

La sintaxis de una union es similar a la de los registros: union etiqueta {

tipo1 dat1; tipo2 dato2; ...

tipoN datoN;

} var1, var2, ..., varN;

y el compilador se encarga de reservar memoria para almacenar el dato de mayor tamaño.

Por ejemplo, dada la siguiente declaración

union mezcla { char c;

int i; float f; } a;

(25)

Punteros (pointers)

El puntero es uno de los tipos de variable más importantes en C. Una variable de tipo puntero es una variable que almacena que almacena la dirección de otra variable que se encuentra en la memoria.

Este tipo de variables se usan en conjunto con los oper-adores & (dirección) y * (contenido). Por ejemplo, consid-érese el siguiente código:

int dato; /* Se declara un dato de tipo entero */ int *punt; /* Se declara un puntero a entero */ ...

punt=&dato; /* punt recibe la DIRECCIÓN del dato */ *punt=5; /* el CONTENIDO de punt es cinco */

Al hacer la asignación punt=&dato, la variable punt alma-cena la dirección de la variable dato. La operación *punt=5 almacena un 5 en el contenido de la variable apuntada por punt, es decir, dato.

Se pueden denir apuntadores a casi cualquier tipo de dato en C. Para ello se puede utilizar la sintaxis

<tipo> *nombre;

(26)

Introducción al C bajo UNIX Punteros (pointers)

Punteros y vectores

Habiendo denido un puntero y un vector del mismo tipo de dato, el puntero puede apuntar a cualquiera de los componentes del vector. Considérese el siguiente código:

char vector[10]; char *puntero; ...

punt=&vector[0]; vector[2]=18; *(puntero+2)=18;

Dado que al denir un vector (array) de N elementos lo que se hace en realidad es denir un puntero a una zona de N elementos, las instrucciones siguientes son equivalentes

vector[2]=18; *(vector+2)=18;

e incluso, dado que vector es una dirección, es posible hacer la asignación puntero=vector;

NOTA: Hay que recordar que un puntero es una variable y un arreglo es una constante que no se puede modicar. Así, la siguiente operación es incorrecta:

(27)

Punteros y memoria dinámica

En C se puede reservar memoria dinámica, es decir, en tiempo de ejecución del programa. Para ello se usa la función malloc(). La función malloc() tiene la sintaxis

p=malloc(tamaño en bytes);

y devuelve un apuntador genérico a una zona de memo-ria con el espacio reservado. Si la localización de memomemo-ria no tuvo éxito, el valor devuelto por malloc() es NULL.

Para reservar memoria para una cierta cantidad N de datos de tipo X y tamaño T, se puede usar la sintaxis

punt=(X *)malloc(N*sizeof(dato));

donde punt es un puntero de tipo X y sizeof() es una función que calcula el tamaño del mismo; en este caso, T.

Existe una función inversa a malloc() y es la función free(), la cual se encarga de liberar la memoria que ha sido asignada mediante una llamada a malloc().

El formato de esta función es simplemente

free(puntero);

(28)

Introducción al C bajo UNIX Punteros (pointers)

#include <stdio.h> #include <stdlib.h>

main(){

struct fecha{ char dia; char mes; int anio; } *punt;

/* Asignación de memoria a punt */ punt=malloc(sizeof(struct fecha)); if (punt==NULL) {

printf("Error: no hay memoria."); exit(1);

}

printf("Día: ");

scanf("%i",&(punt->dia)); printf("Mes: ");

scanf("%i",&(punt->mes)); printf("Año: ");

scanf("%i",&(punt->anio));

(29)

Punteros y cadenas de caracteres

No existe en C ningún tipo de dato especíco para alma-cenar cadenas de caracteres. En lugar de ello, se almacena como vectores de caracteres, a razón de un caracter por cada posición de memoria, colocándose al nal un terminador.

El terminador de cadena se representa por el valor 0

(cero), que es equivalente a la secuencia de escape \0 y la

constante NULL. Por ejemplo, la cadena Hola,soy yo. se representaría en memoria como

72 111 108 97 44 115 111 121 32 121 111 46 0

H o l a , s o y y o . \0

Así, una cadena se almacena como un vector de carac-teres y debe declararse como tal. Por ejemplo, las siguientes declaraciones son válidas

char cad[30];

char cad[30]="Hola,soy yo.";

char cad[30]={'H','o','l','a',',','s','o','y',' ','y','o','.',NULL};

La asignación de una cadena durante la ejecución del pro-grama, no se puede hacer directamente. Es decir, la siguiente sentencia es inválida:

(30)

Introducción al C bajo UNIX Punteros (pointers)

Si se desea hacer una asignación durante la ejecución del programa, existen varias posibilidades. Usando una asig-nación caracter a caracter basada en una notación de vectores quedaría así:

cad[0]='H'; cad[1]='o'; ...

cad[12]=0;

Si se usa una notación basada en punteros, la asignación quedaría así:

*cad='H';

∗(cad+1)='o';

...

∗(cad+12)=0;

La opción más sencilla es usar la función strcpy() de la biblioteca estándar de C. Esta función tiene la sintaxis

char *strcpy(char *s, constante)

y en este caso se puede hacer la operación así

(31)

Salida de una cadena por pantalla

La salida de una cadena por pantalla se puede hacer mediante la función printf() usando el descriptor %s. Por ejemplo,

printf("La cadena es %s", cad);

Entrada de una cadena por teclado

Para leer una cadena por teclado, se puede usar la función scanf() y el descriptor %s como en la siguiente sentencia:

scanf(" %s",cad);

Existe también la posibilidad de leer una cadena usando la función gets(). Esta función tiene la sintaxis

char *gets(char *s)

La función gets() tiene la ventaja de que lee la entrada por teclado hasta encontrar un cambio de línea, mientras que la función scanf() solo lee palabras sueltas, es decir, separadas por espacios.

(32)

Introducción al C bajo UNIX Punteros (pointers)

#include <stdio.h>

main(){

char cad[30]; int i;

printf("Introduce la cadena: "); gets(cad);

for (i=0;cad[i]!=0;i++)

if ( (cad[i]>='A') && (cad[i]<='Z') ) cad[i]=cad[i]+32;

printf("La cadena en minúsculas es: %s", cad); }

Práctica: Usando notación de punteros, hacer un pro-grama que lea una cadena por teclado y la escriba a con-tinuación en pantalla de acuerdo a la siguiente regla: si la cadena tiene N caracteres, se debe desplegar en el orden 1, N, 2, N-1, 3, N-2, ... Por ejemplo, si la cadena de entrada es cartas, la cadena de salida será csaart.

(33)

Funciones de usuario

Una función es un fragmento de código que puede ser in-vocado por otras funciones. Considérese el siguiente ejemplo:

#include <stdio.h>

/* Función para calcular el factorial */ double Factorial(int x){

int i; /* Variable local */

double fac=1; /* Variable local */

for (i=1;i<=x;i++) fac=fac*i; return fac;

}

/* Función principal main() */ main(){

int numero; /* Variable local */

double resultado; /* Variable local */

printf("Introduce un número natural: "); scanf("%i",&numero);

resultado=Factorial(numero);

printf("\n El factorial de %i es %f", numero,resultado);

(34)

Introducción al C bajo UNIX Funciones de usuario

Las funciones en C tienen las siguientes características:

a) Puede recibir cero, uno o varios parámetros. b) Puede usar variables globales y variables locales. c) Puede llamar a otras funciones.

d) Puede regresar un resultado en la llamada.

e) Se les pueden agrupar en bibliotecas de usuario.

Tipo de una función

El tipo de una función es siempre el tipo de dato que devuelve y este tipo debe coincidir con el de la variable que lo recibe. En el ejemplo anterior, ambos son de tipo double.

Sin embargo, también existen funciones que no reciben ni devuelven ningún parámetro. En este caso se usa un tipo de dato especial que es void. Véase la siguiente función:

void EscribeAsteriscos(void) { printf("**********************"); }

La denición de la función, se podría hacer también de-jando los paréntesis vacíos y para llamarla se usaría la sintaxis

EscribeAsteriscos();

(35)

Parámetros de entrada

Según el estándar ANSI C, la denición de los parámetros de entradas se hace en la denición de la función de acuerdo a la siguiente sintaxis:

tipo_func nombrefunc (tipo1 param1, tipo2 param2, ..., tipoN paramN) { /* Cuerpo de la función */

}

donde:

tipo func es el tipo de la función.

nombre func es el nombre de la función. tipo1 es el tipo del parámetro param1.

NOTA: Algunos compiladores no siguen el estándar ANSI y pueden generar el mensaje de error

function prototypes are an ANSI feature

En este caso se tienen dos opciones:

a)Compilar usando un parámetro de línea que fuerce al compilador a seguir el estándar ANSI.

(36)

Introducción al C bajo UNIX Funciones de usuario

Parámetros formales y actuales

Los parámetros con los que se dene una función en C se llaman parámetros formales, mientras que los parámetros con los que se invoca a las funciones se llaman parámetros actuales.

Así, al llamar a las funciones, los valores de los parámetros actuales se copian a la zona de memoria de los parámetros formales, sean aquéllos una variable o una constante.

Paso de parámetros por valor y por referencia En C, los parámetros de llamada a una función se pueden pasar por valor (copia) o por referencia (variable). En el primer caso, se pasa al parámetro formal el valor del parámetro actual de llamada, mientras que en el segundo caso se pasa la dirección de la variable que contiene el valor del parámetro actual.

La principal ventaja del paso de parámetros por referencia es el poder modicar el valor del parámetro actual dentro de la función invocada, lo cual no es posible en el caso del paso de parámetros por valor.

(37)

#include <stdio.h>

/* Función para calcular el factorial */ double Factorial(int *x) {

int i;

double fac=1;

for (i=1;i<=(*x);i++) fac=fac*i; return fac;

}

main(){ /* Función principal main() */

int numero; /* Variable local */

double resultado; /* Variable local */

printf("Introduce un número natural: "); scanf("%i",&numero);

resultado=Factorial(&numero);

printf("\n El factorial de %i es %f", numero,resultado);

}

void Circulo (float radio, float *perim, float *area) {

(38)

Introducción al C bajo UNIX Funciones de usuario

Reglas de ámbito para las variables

Las reglas de ámbito (scope rules) permiten distinguir las zonas de código en las que son accesibles las variables denidas. Esa zona de código se denomina campo o ámbito de validez de la variable.

El campo de validez de una variable, según Kernighan y Ritchie, depende de la parte del programa en la que esté declarada. Así, por ejemplo, para una variable declarada al principio de una función, el campo de validez es la función en la que se declara.

NOTA: Dos variables con el mismo nombre que se han declarado en funciones diferentes no están relacionadas.

Lo anterior vale también para los parámetros formales.

Ámbito de las variables globales

(39)

Paso de estructuras de datos

Respecto al paso y devolución de estructuras de datos, solo hay que tener en cuenta una regla: se debe hacer mediante punteros y no se puede usar una copia.

Considérese el siguiente programa donde se procesa una cadena leída para quitarle todos los espacios en blanco.

#include <stdio.h>

void QuitaEspacios(char *cad1, char *cad2){ int i=0,j=0;

for (i=0;cad1[i]!='\0';i++)

if (cad1[i]!=' ') cad2[j++]=cad1[i]; cad2[j]='\0';

}

main () {

char cadena1[50]; char cadena2[50];

printf("Introduce una cadena: "); gets(cadena1);

QuitaEspacios(cadena1,cadena2); printf("%s", cadena2);

(40)

Introducción al C bajo UNIX Funciones de usuario

Parámetros de main()

Al igual que con las funciones denidas por el usuario, también es posible pasar parámetros a la función main(). En este caso, los parámetros se deben pasar a main() como argumentos en la línea de órdenes.

Para poder utilizar en el programa los argumentos que recibe en la línea de órdenes se debe declarar a main() de la siguiente manera:

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

}

donde argc indica el número de argumentos, incluyendo el nombre del programa, y argv es un vector de punteros a caracter que apuntan a cada uno de los argumentos. El siguiente programa muestra estos parámetros en pantalla:

#include <stdio.h>

main (int argc, char *argv[]) { int i;

printf("Numero de argumentos: %i\n", argc); printf("Argumentos:\n");

(41)

Punteros a funciones

En C existe la posibilidad de pasar funciones como parámetros a otras funciones, lo cual es muy útil para indi-carle a una función que realice su tarea con ayuda de una función u otra, según sea el caso.

Supóngase que tenemos una función llamada Ordenar() que lee un vector de enteros de un chero, lo ordena y lo vuelve a almacenar en él. Esta función podría estar denida de la siguiente manera:

void Ordenar(void) { int vector[15];

... ...

LeerVector(vector); Sort(vector);

EscribirVector(vector); ...

}

(42)

Introducción al C bajo UNIX Funciones de usuario

Otra posibilidad es disponer de una única función Or-denar() que recibiría como parámetro la dirección de la función de ordenación que debe utilizar.

Esa dirección se le puede pasar mediante un puntero a función, el cual tiene la sintaxis:

tipo (*nombre punt)()

donde tipo es el tipo de dato que devuelve la función y nombre punt el nombre del puntero a función.

Por ejemplo, la declaración

int (*punt)()

dene a punt como un puntero a una función que devuelve un entero.

Debe tenerse en cuenta que lo anterior es diferente a

int *punt()

(43)

Si se utiliza un puntero a función y se le pasa a Ordenar(), la declaración de la función quedaría de la siguiente manera:

void Ordenar(void (*punt)()) { int vector[15];

... ...

LeerVector(vector); *punt(vector);

EscribirVector(vector); ...

}

es decir, se le pasa punt como parámetro formal y en el cuerpo de la función se reemplaza Sort por "el contenido de punt", que es la dirección de la función que se desea ejecutar.

Por otra parte, para hacer la llamada de la función ordenar(), y pasarle el parámetro actual que reemplaza a punt, se podría usar el siguiente código

...

if (orden=='a') Ordenar(Sort1); else Ordenar(Sort2);

Figure

Actualización...

Referencias

Actualización...

Related subjects :