Professional Documents
Culture Documents
{
printf("Ei vaior mximo entre 15 y 24 es %d\n.., 15>24)? 15:24)j;
}
Aun cuando la definidn de la macro MAX se asemeja a la defincin de una
fundn, no deben confundirse los conceptos. Las macro slo representan elementos
de sustitucin de texto. Es ms, si ia mac-o contuviera aign error de sintaxis, ei
compilador informara del error en el punto de utilizadn de la maa-o (que es en
realidad donde se expande la misma), y no en e! punta de definicin de !a macro.
Esto dificulta las tareas de depuradn del programa Q.Jando se utlizan macros
complejas. Por lo tanto, conviene utlizar1as con criterio.
Operadores y expresiones -
El lenguaje C debe gran parte de su aceptacin a que es un lenguaje
conciso, ya que se apoya fuertemente en el uso de operadores. Por ejemplo, es
posible realizar na asignacin de valor a una valable al mismo tiempo que se
compara el resultado de esta asignacin con otro valor. Por ejemplo:
if ( ( err = Foo()
return;
== OVERFLOW) {
}
II
En este caso el resultado devuelto por la funcin Foo() es guardado en la variable
err , y luego ste valor es comparado con la constante definida OVERFLOW. Si
coinciden, se ejecuta la sentencia retum y si no se prosigue. El ienguaje tambin
provee operadores de asignadn compuestos como por ejemplo:
a+= 5;
Esta expresin es equivalente a lo que en otros lenguajes se escribirla como:
a = a + s;
Debemos notar Que el signo '=, significa asignacin y por lo tanto la operacin de
comparacin se realiza con el operador '=='. Un error muy comn cuando se
comienza a programar en C habiendo programado en otros ienguajes como Basic,
se comete en las expresiones de evaluacin de la sentenda if:
11
if (a = 5) {
II...
}eise{
return;
}
En este caso la sentencia retum de la clasula else nunca se ejecutar, ya que en
la expresin de la sentencia if, siempre se asigna el valor 5 a la variable a y por lo
tanto la condicin siempre resulta veidadera (true). El compilador no dar ningn
error, ya que como vimos antes, la expresin de asignacin puede aparecer en
cualquier lado, incluso como parte de la expresin de evaluacin de una sentencia
if. El cdigo anterior debera haberse escrito como:
if (a == 5) {
II...
}eise{
return:
}
la desigualdad se evala con el operador '1=', por ejemplo:
if (a 1= S) {
II...
}else{
return;
}
El operador '!' denota negadn, por ejemplo, lo que en Basic se escriba como:
IF NOT (A = B) THEN
END IF
en c se puede escribir como:
if ( !( A == B) ) {
II...
}
Por eso el operdor '1=' se lee en ingls como not equal.
Tambin disponemos de los operadores incremento y decremento unitario,
'++' y ,--, respectivamente. Por ejemplo la expresin ++b es equivalente a b = b
+1, del mismo modo que --b es equivalente a b = b- 1. Estos operadores pueden
aparecer como prefijos o como sufijos: ++b 6 b++. La diferencia queda de
manifiesto en las asignaciones, por ejemplo:
12
b = S;
c = ++b;
en este caso c valdr 6, y b valdr 6; mientras que:
b = S;
c = b++
har que c valga 5, y b valga 6.
Conversin de tipos
En una expresin en la que aparezcan operadores binarios, los operan dos
pueden ser de distinto tipo (por ejemplo, un operando es del tipo float y el otro es
de tipo int). El lenguaje C I lO es un lellguaje fuertemente tipado (strollgly-typedj,
por lo que el compilador aplicar ciertas reglas de conversin automtica mente a la
hora de evaluar las expresiones. Estas reglas se orientan para convertir el operando
de menor precisin hacia el tipo de dato del operando de mayor predsin. Por
ejemplo, si uno de los operandos es float y el otro es double, el primero se
convertir en la memoria intermeia a doubie al momento de evaiuar ia expresin,
y el tipo del resultado tambin corresponder al tipo de mayor precisin. Por
e iQ m nln.
,,_. ..'_.
float a= 5.3;
double b = 7.8;
la expresin a+b, hace que el valor de a se convierta a double antes de sumarle
b. El resultado es de tipo double. Notar que el tipo de dato de la variable no
cambiar, sino que durante la evaluacin de la expresin el compilador tratar a ios
operandos como si fueran del tipo double.
D~I mi
smo m o Si uno d~ Ioc nn~~n dos es "'~I t-.l no -1...t 'I ~I nt-r o es del t-i
po u-..:, ~, -_.~,.. ..,_.~~. .~.
double, el primero se convertir a double antes de llevar a cabo la operadn.
Las operaciones en las que intervienen operandos de distinto tipo se
denominan operaciones en modo mixto, y se aplican las reglas de conversin
mencionadas que pueden sintetizarse en base a un orden de prioridades para los
tipos invoiuct-ados:
long double (mayor prioridad)
double
float
long int
int
short
char (menor prioridad)
la idea de este orden de prioridad es garantizar la mayor precisin de la operacin.
Sin embargo, observemos el siguiente ejemplo:
13
int valorl_int = 3:
int valor2_int = 4;
fioat valor_float = 7.0;
float resultado_float;
resultado_float = valor _float + valorl_int I valor2_int;
Cul es el resultado esperado para la variable resultado_float? La respuesta, es
que resultado_float valdr 7.0. La causa de esto, es que el cociente en este caso no
es una expresin en modo mixto y por lo tanto ai resultado 0.75 se convierte a
entero truncando la parte decimal. Luego este valor entero igual a 0 es convertido a
f!oat para evaluar la suma. Pero qu hubiera sucedido si \Jalor2_int se hubiera
declarado como float? En este caso el codente hubiera sido una operacin en modo
mixto y valorl_int se habra convertido a float dando el resultado pardal 0.75 de
tipo tambin float. En consecuencia, la adicin ya no es una operacin en modo
mixto y por lo tanto el valor almacenado en resultado_float hubiera sido 7.75.
El hecho de que erl el caso de operaciones en modo mixto, el compilador rlo
exija una conversin explcita de los tipos de datos, hace que el lenguaje C sea
clasificado como un lenguaje dbilmente tipado (weakly-typed) en contraposicin a
los lenguajes fuertemente tipados (strongly-typed) que no permiten operadones en
modo mixto.
la conversin expidta de tipos (castingj es una buena prctica que ayuda a
evitar los errores por truncamiento. La conversin expldta de tipos se lleva acabo
por medio de! operador cast. Siempre que querramos cambiar temporalemente el
formato de una variable, simplemente pondremos delante del identificador de la
variable el tipo entre parntesis al que queremos convertirla. Por ejemplo, para
evitar el error de truncamiento del ejemplo, al haber definido valorl_int y
valor2_int como del tipo entero, podremos usar cualquiera de las siguientes
expresiones eqivalentes:
resultado_float = valor_float + (float)valorl_int / valor2_int;
resultado_float = valor_float + valorl_int / (float)valor2_int;
resuitado_float = vaior_float + (fioat)vaiorl_int I (fioat)vaior2._int;
las sentencias de control evalan una condidn y en base al resultado
controlan la ejecucin del cdigo que las conforman. En C existen las siguientes
estructuras de control ;
if/else y el operador ?:
while
do-while
for
switch
A continuacin se describe la sintaxis de las distintas sentencias de control :
14
la sentencia if I else
if { condicion ) {
II cdigo a ejecutar si la condicin es verdadera
}else{
II cdigo a ejecutar si la condidn es false
}
La condicin se considera verdadera si posee un valor distinto de cero, y falsa en
caso contrario. La condicin siempre debe estar encerrada entre parntesis ya
continuacin ie puee seguir una sentencia e expresin o una sentencia
compuesta. Los siguientes ejemplos son formas vlidas para la sentencia if:
if ( a == S) printf("EI valor de a es s\n");
if { a== 5j
pri"tf{"EI valor de a es S\"N);
if (a > 7)
a= O;
eise
++a;
if (a> 7) a= O; else ++a;
if (a < O) {
printf("EI valor de a no puede ser negativo\nW);
a= 7;
}
if (a < O) {
printf("EI valor de a no puede ser negativo\nW);
a:;; 7;
} else {
--a:
}
if (a < O) {
printf(-EI valor de a no puede ser negativo\nR);
a= 7:
}
else {
--a:
}
Relacionada con la sentencia if/else se encuentra el operador ?:, el mismo
es similar ala fund6n IIF de otros lenguajes:
condidon? resultado.verdadero : resultado falso
15
Por ejemplo,
a= (b < 7? O: CheckStatus(2;
La sentencia while
la sentencia while repite la ejecudn de una sentenda de expresin o de
una sentencia compuesta mientras la condicin sea verdadera:
while (condicion) {
II cdigo a repetir mientras la condicin sea verdadera
}
a= 10;
while (a>O) {
printf("%d\n", a);
--a;
}
while (OpenDoor( SoundAlarm();
Es posible forzar la salida del bucle por medio de la instruccin break. Por ejemplo:
while (a>O) {
if (SalirAntes( break;
--a;
}
Si la fundn salirAntesO devuelve un valor verdadero antes de que la condicin del
bucle while sea falsa, se fuerza la salida del bucle.
Tambin es posibie controiar el flujo de ejecudn dentro dei bucle, con ia
instrucdn continue, que fuerza el salto hada la lnea que verifica la condidn,
ignorando e! cdigo que la sucede. Por ejemplo:
while (condicion) {
II hacer algo...
if (a == 7) continue;
II hacer algo ms...
}
En este caso, si mientras la ejecucin del bucle, la variable 'a' toma el valor
7, se saltea la ejecucin de las expresiones que estn a continuadn, y se vuelve al
principio del bucie.
16
La sentencia do-while
La sentencia while evalua la condidn antes de ejecutar el cdigo de la
sentenda asodada. En ocasiones puede ser necesario que el d1equeo de la
condicin se realice luego de haber ejecutado al menos una vez el cdigo dentro del
bude. Por ejemplo:
do {
a= IngresarValor()
} while (a < O! ! a> 7);
En este ejemplo, primero se pide que se ingrese un valor y el valor ingresado se le
asigna a la variable 'a'. Luego, esta operacin se repite mientras 'a' no se encuentre
en e: intervalo de O a 7.
Tambin puede utilizarse la instruccin break para forzar la salida del bucle.
La sentencia for
La sentencia for en C es ms potente que en otros lenguajes convencionales. Su
sintaxis es:
for {i = O; i< 20; ++i) {
II...
}
la sentencia for se basa en tres expresiones:
la primera es una expresin de asignacin de vaior inidai.
la segunda es la condicin que debe cumplirse para que el ciclo se repita.
La tercera es una expresin de alteracin de la variable Que controla el
bude.
Como en el caso de la sentena while, la condidn se mequea al comienzo del
bude, por lo que s la condin es falsa de entrada, no se ejecutar el cdigo de la
expresin asodada .
Pero, por qu decimos que es ms potente que en otros lenguajes? Esto se debe a
que las expresiones pueden ser de lo ms variadas y no necesariamente deben
estar asodadas a valores numricos. Por ejemplo, un bucle infinito puede
expresarse como :
for (;;) {
1100
}
ves fundonalmente eauivalente a:
17
while(true) {
II...
}
Obviamente en estos casos, la salida del bucle deber ser forzada a travs de la
instruccin break por alguna condidn adicional verificada dentro del bucle.
Tambin puede utilizarse la instrucdn contjnue para saltear parte del cdigo
dentro del bucle.
Pero e! bucle for tambin puede fom1arse con otros tipos de expresiones:
for (int i= O; CheckStatus() != 0; --i) {
II...
}
En este caso el bucle terminar cuando la funcin CheckStatus() devuelva un valor
igual a cero. Mientras tanto la variable i se ir decrementando sin tener un tope
definido.
La sentencia switch
La sentencia switch se utiliza para evaluar un conjunto de expresiones
seleccionadas de acuerdo a un valor entero. Por ejemplo:
switch (variable) {
case I :
ii expresiones que se ejecutan cuando variable == 1
break;
case 2:
II expresiones que se ejecutan cuando variable :: 2
break;
case 'a':
case 'e':
case 'j':
case '0':
case 'u':
II expresiones que se ejecutan cuando variable es una vocal
/ / minscula.
break;
default:
I I expresiones que se ejecutan cuando variables tiene un valor
i i diferente a cuaiquiera de ios especificados en ias dasulas
II case.
}
Con esta sentencia deben notarse dos cosas. Primero que los valores de las
clasulas case deben ser constantes enteras. Por ejemplo, el siguiente cdigo es
ilegal:
18
switch (variable) {
case 0.75:
II...
break"
,
case x:
II...
break;
}
El compilador dar error ya que en el primer case el valor espedficado no es un
entero y en el segundo caso porque no es una constante, sino que se intent
utiiizar el valor de una variabie.
la otra observacin concierne a la instrucdn break. la misma debe estar
presente para separa los case a menos que se !os quiera agrupar para tratarlos
todos de la misma manera como en el primer ejemplo, en el que para todos los
valores que correspondan al cdigo ASCII de una vocal minscula, se ejecutar el
mismo cdigo. Un error comn, que suele ser difdl de encontrar, es justamente el
que se comete al olvidar una instruccin break, especialmente cuando la sentencia
switch es muy grande.
Punteros
las variables del tipo puntero constituyen una de las grandes diferencias del
lenguaje C con otros lenguajes de programacin. las variables puntero permiten
acceder de manera direc'-La al contenido de la memoria donde se encentran
almacenadas variables y funciones. Esta caracterstica es la que hace al lenguaje C
tan potente y al mismo tiempo tan odiado por sus detractores. El motivo de esto
ltimo, es que cuando las variables puntero no se tratan adecuadamente dentro del
cdigo, se pueden produdr errores muy difciles de detectar y que harn que la
apicacin falie, cueigue el sistema o ei mismo se comporte de manera errtica bajo
ciertas condiciones. Este tipo de errores suele ser la pesadilla de los
pron~ m~dnr-Qs ,,~que la d~ ecc i O." i m"li ca U" a"~licic r u i d~dnco Ii&o l r.! i go e"
.~."' .-~.- , 7- -~- ..., "I"'. , -.-~- ---"'"...' ,.
busca de errores lgicos. Por lo tanto, es conveniente comprender bien el tema ya
que la mejor solucin para evitar los problemas es prevenirlos.
Variables puntero
Una variabie puntero representa aimacena ei vaior de una direccin de
memoria y por lo tanto Su tamao es de 4 bytes y es equivalente a un long. Las
variables puntero se declaran indicando el tipo de! dato contenido en la direccin de
memoria a la que apuntan:
float *pFloat;
En este caso el * se puede considerar que forma parte del tipo de dato, de manera
que la declaracin anterior significa que la variable pAoat es un puntero a una
direcdn de memoria donde se encuentra aimacenado un vaior dei tipo float.
Algunos autores prefieren indicar esto escribiendo:
19
float* pFloat;
Notar que en este caso el * se encuentra pegado al tipo de datos, sin embargo, si
en una misma lnea se intenta declarar varias variables puntero la fonTla correcta
es:
float *pAoatl, *pAoat2, *pAoat3;
La asignacin de un valor a una variable puntero se realiza haciendo uso del
operador '&' llamado operador direcdn. Por ejemplo:
float x= 5.0;
float *pValor;
pValor = &x;
En este caso el operador '&' aplicado a la variable 'x' devuelve su direccin
de memoria, y esta direccin se almacena en la variable pValor. Ahora que pValor
contiene ia direccin de memoria donde se encuentra aimacenado el vaior de ia
variable 'x', podemos modificar el valor de 'x' a travs del puntero pValor utilizando
el operador ,*, denominado operador desreferenca:
*pValor = 7.5;
Con esta expresin indicamos que el contenido de la posicin de memoria
apuntada por la variable pValor debe contener ahora el valor 7.S. Ahora la variable
'x' valdr 7.5.
Debemos notar el doble uso que tiene el smbolo '*'. En una declaracin de
variable, indica que la variable es un puntero. En una sentencia de expresin, indica
la devoludn del contenido de una posicin de memoria. Se pueden declarar
punteros de cualquier tipo, induso de tipos definidos por el usuario. Tambin
pueden declararse punteros a funcin, es dedr variables que contienen la direccin
en memoria de una funcin.
El valor de una valiable puntero puede compararse con la constante definida
NULl que tiene el valor OL, para determinar si su valor es nulo. Un error muy
comn, es intentar modificar el contenido de la direccin de memoria 0000:0000
por no verificar previamente que el puntero era distinto de este valor. En este caso,
lo ms probable es que la aplicacin falle.
Pero ios punteros no seran tan tiies, si no tuvieran un igebra asociada.
Internamente los punteros son equivalentes a los arrays. Es decir, cada vez que se
define un array e! compilador lo tratar como un puntero. Tal es as, que se
pueden tratar como trminos intercambiables dentro del programa. Por ejemplo,
supongamos que definimos un array de S elementos a entero:
int arreglo[S] = { O, 0, 0, 0, 0 };
:)n