Download - C ++ Estatutos ciclos
��������������� �� ��� ��������������� ���
���������������� ����� � �� �
3. Lenguaje de Programación C
3.1. Lenguajes de Programación: Definición y Características.
3.2. Anatomía de un Programa en C.
3.3. Variables y Constantes.
3.4. Tipos de Datos Básicos en C.
3.5. Operadores Básicos en C.
3.6. Sentencias de Entrada y Salida.
3.7. Estructuras de Control.
3.7.1. Iteración.
3.7.2. Selección
3.8. Tipos de Datos Construidos.
Dennis Ritchie de AT&T Bell Laboratories inventó e implementó el primer C en un DEC PDP-11 que usaba el sistema
Operativo UNIX. El Lenguaje C es el resultado de un proceso de desarrollo de un lenguaje llamado BCLP. Dicho lenguaje
fue desarrollado en 1967 por Martin Richards con la idea de escribir software y compiladores en lenguaje ensamblador. A su
vez el BCLP influyo en un lenguaje llamado B que invento Ken Thompson en 1970, este lenguaje surge para crear las
primeras versiones del sistema operativo UNIX y se instalo en una máquina PDP-7, por los laboratorios BELL.
C surge de la necesidad de re-escribir el UNIX en un lenguaje de alto nivel, pero que algunos procedimientos se
manejaran a nivel ensamblador, es entonces cuando se comienza a desarrollar un lenguaje que solucionara dicho problema.
C es un lenguaje de aplicación general que sirve para escribir cualquier tipo de programa, pero su éxito y popularidad
están íntimamente ligados al sistema operativo UNIX. Si se quiere dar mantenimiento a un sistema UNIX, es preciso emplear
C.
C y UNIX se acoplaron tan bien que pronto no sólo los programas de sistemas, sino casi todos los programas
comerciales que se ejecutaban bajo UNIX se escribieron en C. Este lenguaje se volvió tan popular que se escribieron
versiones de él para otros sistemas operativos populares, así que su uso no está limitado a las computadoras que utilizan
UNIX.
Características del lenguaje C.
• C es conocido como un lenguaje de nivel medio.
Nivel medio no tiene propiamente un significado negativo, no significa que C sea menos poderoso, manejable o
menos desarrollado que un lenguaje de alto nivel, tampoco implica que C es similar a un lenguaje ensamblador con
sus problemas asociados. Se considera a C un lenguaje de nivel medio, ya que combina elementos de lenguajes de
alto nivel con la funcionalidad y potencialidad del ensamblador.
El sistema operativo UNIX se inició en una DEC PDP-7, en los Laboratorios Bell durante 1969. En 1973, Ritchie y Thompson re-escribieron el kernel (núcleo) de UNIX en C, rompiendo así con la tradición de que el software de sistemas está escrito en lenguaje ensamblador. Hacia 1974 fue introducido en las universidades "con fines educacionales" y al cabo de pocos años estaba ya disponible para uso comercial. El UNIX es un sistema portable (se ejecutan en una extensa variedad de computadoras), flexible, potente, con entorno programable, multiusuario y multitarea
��������������� �� ��� ��������������� ���
���������������� ����� � �� �
Lenguajes de Alto nivel Lenguajes de Nivel Medio Lenguajes de Bajo nivel
Ada C Macro-assembler
Modula-2 Forth Assembler
Pascal
COBOL
FORTRAN
BASIC
• El código C es muy transportable.
Esto quiere decir que se puede adaptar el software escrito para un tipo de computadora a otro tipo.
• Esta basado en funciones.
• Trabaja directo con bits, bytes y direcciones de memoria.
• Es un lenguaje diseñado por programadores para programadores.
Da al programador lo que el programador quiere: pocas restricciones, pocas quejas, código rápido y eficacia.
• No esta orientado a un área en particular.
• Posee 32 palabras claves (27 Kernighan y Ritchie, 5 ANSI)
• C es un compilador, (BASIC es un interprete). Intérprete: Lee el código fuente de su programa de línea en línea y realiza las instrucciones específicas que están en esa línea.
Compilador: Lee el programa entero y entonces lo convierte en código objeto, que es una traducción del código fuente, de forma que el compilador puede
ejecutarlo directamente.
• Maneja una biblioteca estándar.
Dicha biblioteca esta formada por un conjunto de archivos donde cada archivo esta formado por un grupo de
funciones y/o procedimientos que permiten resolver problemas en forma rápida y eficiente.
• Define seis etapas para ejecutar un programa:
�� Etapa de Edición.
�� Etapa de pre-procesamiento.
�� Etapa de compilación.
�� Etapa de encadenamiento.
�� Etapa de carga.
�� Etapa de ejecución.
ANSI C estándar.
En 1978 Prentice Hall publica el libro “The C Programming” dando a conocer dicho lenguaje. Con la popularidad
creciente de las microcomputadoras, se crearon muchas implementaciones nuevas. En lo que se podría llamar un milagro, la
mayoría de estas implementaciones eran altamente compatibles entre sí a nivel de código fuente. Sin embargo, como no
existía un estándar, siempre había algunas discrepancias. Al principio del verano de 1983 se estableció un comité para
encargarse de esta situación. Este comité comenzó a trabajar en un estándar ANSI que definiría el lenguaje C.
En 1989 fue aprobado el lenguaje estándar de C, llamado ANSI C. El estándar fue dado a conocer en Estados Unidos
por el organismo ANSI y a nivel mundial por la organización ISO.
��������������� �� ��� ��������������� ���
���������������� ����� � �� �
El estándar ANSI C es un superconjunto del estándar UNIX, los programadores que migren de un compilador basado en
UNIX a uno con estándar ANSI encontrarán todas las características que habían usado.
Palabras clave de C.
Existe una clase especial de identificadores, llamados palabras clave o palabras reservadas, que en C y C++ tienen un
significado predefinido y que no podemos emplear como nombres de variables ni de ninguna otra cosa.
Palabras Clave C
auto do goto short typedef
break double if signed union
case else int sizeof unsigned
char enum long static void
const extern register struct volatile
continue float return switch while
default for
Identificadores: se usan como nombres de variables y otros elementos de un programa en C++. Un identificador debe comenzar con una letra o bien con el símbolo de subrayado, y los demás caracteres deben ser letras, dígitos o el símbolo de subrayado. Tomar en cuenta que estos deben ser mnemónicos. Anatomía de un programa en C.
Un programa en C se compone de una o más funciones. Una de las funciones debe ser obligatoriamente main. Una
función es un grupo de instrucciones o sentencias que realizan una o más acciones. Así mismo, un programa contendrá una
serie de directivas #include que permitirán incluir en el mismo archivos de cabecera que, a su vez constarán de funciones y
datos predefinidos en ellos.
Formato general:
/* Comentarios */ #include archivo /*directivas del preprocesador */ #define /*macros del preprocesador*/ Declaración de variables globales Declaración de funciones main() { declaración de variables locales secuencia de sentencias o cuerpo del main return 0; } funcion1() { }
��������������� �� ��� ��������������� ���
���������������� ����� � �� �
/* */
Encierran los comentarios de lo que el programa realiza, se pueden utilizar en cualquier parte del mismo. Es muy importante
ir documentado cada uno de los pasos del programa y sobre todo al inicio indicar que es lo que realiza así como su autor.
Estos símbolos representan comentarios tanto en C como en C++. En C++ también podemos utilizar // para indicar un
comentario pero el mismo terminara al final de la línea. Con /* y */ nos podemos extender por varias líneas.
Directivas:
El pre-procesador de C maneja las siguientes directivas:
#if , #else, #elif, #endif
Permiten compilar porciones del código fuente del programa selectivamente. Tienen la misma función que el if, else, endif
tradicional.
#ifdef, #ifndef
Método de compilación condicional que significa <<si definido>> y <<si no definido>> respectivamente.
#include
Instruye al compilador para incluir otro archivo fuente. El archivo debe encerrase entre paréntesis de ángulo o entre comillas
dobles. Ejemplo:
#include <stdio.h>
#define
Se usa para definir un identificador y una cadena que el compilador sustituirá por el identificador cada vez que se encuentren
en el archivo fuente. Dicha sustitución puede ser por un valor simple o por algo tan elaborado como el cuerpo de una función.
Al identificador se le llama MACRO y al proceso de reemplazamiento se le llama SUSTITUCION DE LA MACRO.
Ejemplo: #define TRUE 1
Esto provocara que el compilador sustituya 1 cada que encuentre TRUE
#undef
Se usa para quitar una definición del nombre de macro que sigue y que se hizo antes.
#line
Se usa para cambiar los contenidos de _ _LINE_ _ y _ _FILE_ _, que son identificadores predefinidos.
#error
Fuerza al compilador a parar la compilación cuando se encuentra.
#pragma
��������������� �� ��� ��������������� ���
���������������� ����� � �� �
Es una directiva definida por la implementación que permite dar diversas instrucciones, definidas por el creador del
compilador, al compilador.
Declaración de Variables Globales y Funciones:
Representa la parte declarativa del programa. Las variables declaradas en esta parte se podrán utilizar durante todo el
programa, incluyendo dentro de las otras funciones.
main():
Indica el encabezado de la función principal del programa y el cuerpo del mismo deberá estar encerado entre paréntesis de
llave {}.
Declaración de variables locales:
Representa la parte declarativa de las variables que serán utilizadas únicamente en esta función.
Cuerpo del main:
Representan a las instrucciones ejecutables necesarias para la solución a un problema.
Funcion1():
Representa a una o mas funciones creadas por el programador para estructurar el programa.
Notas importantes:
• Toda sentencia o instrucción en un programa debe de terminar con punto y coma.
• Puede haber más de una instrucción por línea siempre y cuando vayan separadas por puntos y coma.
• Los comentarios deberán ser claros y concisos. Se puede utilizar un numero variable de los mismos siempre y cuando el programa
así lo requiera.
Variables y Constantes
Tipos de datos de C.
Los operadores manipulan variables constantes para formar expresiones. Estos cuatro: variables, constantes,
operadores y expresiones son el abecedario del lenguaje C.
• Variables: es una posición de memoria con nombre que se usa para mantener un valor que puede ser modificado
por el programa. Todas las variables en C han de ser declaradas antes de poder ser usadas. La forma general de
declaración es: tipo lista_de_variables;
• Constante: se refieren a valores fijos que no pueden ser alterados por el programa. La forma en que se
representan depende de su tipo.
• Operador: es un símbolo que le dice al compilador que realice manipulaciones matemáticas o lógicas especificas.
Existen cuatro clases de operadores: Aritméticos, Relacionales, Lógicos y a nivel de bits.
• Expresiones: Cualquier combinación valida de los elementos atómicos de C: los datos (variables o constantes) y los
operadores.
��������������� �� ��� ��������������� ���
���������������� ����� � �� �
En C existen cinco tipos básicos de datos, Carácter (char), Entero (integer), Flotante (float), Doble flotante (double), vacío
(void).
• char: Carácter. Se usa para guardar caracteres de 8 bits.
• int: Entero. Se usa para guardar cantidades enteras. También se usan para controlar los bucles y las sentencias
condicionales.
• float y double: Flotante y doble flotante. Se usan para guardar valores que necesiten componente fraccional. La
diferencia entre ellos es la magnitud del numero.
• void: vacío. Se usa para mejorar la comprobación de tipos. Declarar explícitamente una función como que no
devuelve valor alguno. Declarar una función explícitamente sin parámetros. Para crear puntero genéricos.
La siguiente tabla muestra el tamaño y el rango de valores para los cuales están definidos cada uno de los tipos de datos:
Tipo Ancho Rango
Char 8 bits o 1 byte 0 a 255 o –128 a 127
Int 16 bits o 2 bytes -32768 a 32767
Float 32 bits o 4 bytes 3.4 E – 38 a 3.4 E + 38
Double 64 bits o 8 bytes 1.7 E –308 a 1.7 E +308
Void 0 Sin Valor
Modificadores de Tipo:
Excepto para void, los tipos de datos básicos tienen varios modificadores que los preceden. Se usa un modificador
para alterar el significado de un tipo para que se ajuste a las necesidades de cada momento. Dichos modificadores son:
Signed (con signo), Unsigned (sin signo), Long (largo), Short (corto).
La siguiente tabla muestra todas las combinaciones que se ajustan al estándar ANSI. Es importante recordar que
los datos de la tabla son valores muestra para dar una idea general de las diferencias entre tipos, ya que las máquinas
pueden emplear una longitud de palabra de 16, 32 o 64 bits y esto produciría cambios significativos a dicha tabla.
Tipo Tamaño aproximado Rango mínimo Char 8 bits ó 1 byte -128 a 127
Unsigned char 8 bits ó 1 byte 0 a 255 Signed char 8 bits ó 1 byte -128 a 127
Int 16 bits ó 2 bytes -32,767 a 32,767 Unsigned int 16 bits ó 2 bytes 0 a 65,535
Signed int 16 bits ó 2 bytes -32,767 a 32,767 Short int 16 bits ó 2 bytes -32,767 a 32,767
Unsigned short int 16 bits ó 2 bytes 0 a 65,535 Signed short int 16 bits ó 2 bytes -32,767 a 32,767
Long int 32 bits ó 4 bytes -2,147,483,647 a 2,147,483,647 Signed long int 32 bits ó 4 bytes -2,147,483,647 a 2,147,483,647
Unsigned long int 32 bits ó 4 bytes 0 a 4,294,967,295 Float 32 bits ó 4 bytes Seis dígitos de precisión
Double 64 bits ó 8 bytes Diez dígitos de precisión Long double 128 bits ó 16 bytes Diez dígitos de precisión
��������������� �� ��� ��������������� ���
���������������� ����� � �� �
La diferencia entre los enteros con y sin signo es la forma en que la computadora interpreta el bit más alto (más
significativo) del entero. Si se especifica un entero con signo, entonces el compilador genera código que asume que el bit
más significativo va a ser usado como indicador de signo.
En el caso de los números con signo. Si el identificador de signo es 0 entonces el numero es positivo; si es 1
entonces el numero será negativo.
Los enteros con signo son importantes en muchos algoritmos, pero sólo tienen la mitad de la magnitud absoluta que
el correspondiente sin signo. Por ejemplo:
01111111 11111111 corresponde al 32767
Si el primero fuese 1, estaríamos hablando del mismo numero negativo.
En el caso de que el número fuese unsigned y el mas significativo fuera 1 entonces representaría el numero 65535.
Modificadores const y volatile.
Los números presentan dos problemas en los programas de computadora. El primero es que no poseen valor
mnemónico. Por ejemplo, si encontramos el numero 10 en un programa, nada nos dice acerca de su significado. Si el
programa se usa en un banco, podría tratarse del número de sucursales o del número de ventanillas en la oficina matriz.
Para entender el programa, necesitamos conocer el significado de cada constante. El segundo problema es que si es
necesario cambiar algunos números de un programa, la modificación suele introducir errores. Supongamos que el 10 ocurre
doce veces en el programa del banco, y que en cuatro ocasiones representa el número de sucursales y en las otras ocho
ocasiones representa el número de ventanillas en la oficina matriz. Si el banco abre una nueva sucursal y se hace necesario
actualizar el programa, hay una buena posibilidad de que algunos de los 10 que deberían cambiarse a 11 no se cambien, y
que algunos de los que no deberían cambiarse sí lo hagan. La forma de evitar estos problemas es dar un nombre a cada
número y utilizar el nombre en lugar del número dentro del programa. Por ejemplo, el programa del banco podría tener dos
constantes con los nombres NO_SUCURSALES y NO_VENTANILLAS. Ambos números podrían tener el valor 10, pero en el
momento en que el banco abre una nueva sucursal lo único que hay que hacer para actualizar el programa es modificar la
definición de NO_SUCURSALES.
Sin embargo, este método de nombrar constantes numéricas tiene un problema; podríamos modificar
inadvertidamente el valor de una de estas variables. C++ ofrece un mecanismo para marcar una variable inicializada de
modo que no se pueda modificar. Si el programa trata de modificar una de estas variables, se produce una condición de
error. Este mecanismo es llamado const y tiene las siguientes características.
El modificador de tipo const se utiliza para informar al compilador de que el valor de la siguiente variable no puede
ser cambiado por ninguna sentencia del programa. (Sin embargo debe ser inicializada). Por ejemplo:
const int NO_SUCURSALES = 10
��������������� �� ��� ��������������� ���
���������������� ����� � �� �
Informa al compilador de que NO_SUCURSALES no puede aparecer en la parte izquierda de una sentencia de
asignación y fija el valor inicial a 10.
Una variable const recibe su valor bien por una inicialización explícita o bien por algún medio dependiente del
hardware.
Las variables de tipo const tienen dos propósitos principales. En primer lugar, permitir al compilador situarlas en
ROM (memoria de sólo lectura). En segundo lugar, permiten asegurarse de que cuando pasan punteros a una función,
ninguna parte del código de la función pueda modificar los objetos a que apuntan.
Nota: Una variable que se declara usando el modificador const se conoce como constante declarada. El lenguaje C no exige escribir las
constantes declaradas exclusivamente con mayúsculas, pero ello es una práctica estándar entre los programadores en C.
volatile:
Se utiliza para decir al compilador que la variable que le sigue puede ser modificada de alguna forma que no sea
bajo el control del programa. Por ejemplo la dirección de una variable global puede ser pasada a la rutina de reloj del sistema
operativo y usada para mantener el tiempo real del sistema o bien una variable puede ser actualizada por el reloj del sistema
cada 10 segundos.
El propósito de declarar una variable como volatile es evitar ciertas optimizaciones del compilador que pudieran
evitar el acceso al valor de la variable según indique una expresión. Esto es muy importante porque la mayoría de los
compiladores de C automáticamente optimizan ciertas expresiones asumiendo que el contenido de una variable no cambia si
no aparece en la parte izquierda de una sentencia de asignación; así, puede que no se vuelva a comprobar la variable cada
vez que se referencia. Además algunos compiladores cambian el orden de evaluación de una expresión durante el proceso
de compilación. El modificador volatile previene que se hagan esos cambios.
Nota: Es posible usar const y volatile juntos. Por ejemplo, si se asume que 0x30 es el valor de un puerto que cambia por
una condición externa exclusivamente, entonces la siguiente declaración es precisamente la que se necesita para prevenir
cualquier posibilidad de efectos secundarios accidentales:
const volatile unsigned char *puerto=0x30;
Conversiones de Tipos de Datos.
La mayoría de los programas procesan información de varios tipos. A veces todas las operaciones son del mismo
tipo. Por ejemplo, la adición de un entero a otro produce un entero. Pero con frecuencia es necesario convertir datos de un
tipo a otro. La conversión de tipos se refiere a la situación en la que variables de un tipo se mezclan con las de otro tipo.
Cuando se da esta mezcla en la sentencia de asignación, la regla de la conversión de tipo es sencilla; C convierte el valor del
lado derecho (lado de la expresión) de la asignación al tipo del lado izquierdo (la variable destino).
Cuando se convierten desde enteros hasta caracteres, los enteros largos a enteros y los enteros a enteros cortos, la
regla básica que se sigue es la cantidad apropiada entre los bits más altos se quitará. Así se perderán 8 bits cuando va
desde un entero hasta un carácter o un entero corto y 16 bits cuando va de un entero largo a un entero.
��������������� �� ��� ��������������� ���
���������������� ����� � �� �
La siguiente tabla sintetiza estas conversiones de tipo. Se debe recordar un punto importante: la conversiones de
un int a un float, un float a un double, y así sucesivamente, no añadirá precisión o rendimiento. Estas clases de conversiones
sólo cambiarán el formato en que se representa el valor.
Hacia Desde Posible pérdida de Información
char Unsigned char Si el valor > 127, el objetivo será negativo.
char Short int Mayor de 8 bits
char Int Mayor de 8 bits
char Long int Mayor de 24 bits
short int Int Ninguna
short int Long int Mayor de 16 bits
Int Long int Mayor de 16 bits
Int Float Parte fraccional y posiblemente más
float Double Precisión, resultado redondeado
double Long double Ninguna
Para hacer la conversión que la tabla no muestra directamente, simplemente convertir un tipo de un momento hasta
el final. Por ejemplo, para convertir desde un double hasta un int, primero convertimos de double a float y entonces de float a
un int.
Operadores.
C es un lenguaje muy rico en operadores incorporados. C define cuatro clases de operadores: aritméticos,
relacionales, lógicos y a nivel de bits.
Aritméticos: Los operadores de suma, resta, multiplicación y división funcionan de la misma manera en C que en la mayoría
de los lenguajes de computadora. Cuando se aplica la división a un entero, la computadora truncará cualquier resto. El
operador módulo de la división almacena el resto de una división entera. Sin embargo, no se puede usar % sobre los tipos
float o double.
Símbolo Operación Símbolo Operación
- Resta % Módulo división
+ Suma -- Decremento
* Multiplicación ++ Incremento
/ División
Es importante mencionar que el tipo de valor producido y el valor exacto del resultado dependen de los tipos de
números que se están combinando. Si ambos operandos son de tipo int, el resultado de combinarlos con un operador
aritmético es de tipo int. Si uno de los operadores, o ambos, son de tipo double, el resultado es de tipo double.
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
El tipo del resultado puede ser más importante de lo que podríamos pensar. Por ejemplo, 7.0/2 tiene un operando
de tipo double, por lo tanto el resultado es el número de tipo double 3.5. En cambio, 7/2 tiene dos operandos de tipo int, y por
ello produce el resultado de tipo int 3. Aunque la parte fraccionaria del resultado valga cero, hay una diferencia. Por ejemplo,
6.0/2 tiene un operando de tipo double, por lo tanto, el resultado es el valor de tipo double 3.0, que sólo es una cantidad
aproximada. En cambio, 6/2 tiene dos operandos de tipo int; por lo tanto, produce el resultado 3 que es de tipo int y por lo
mismo es una cantidad exacta. El operador de división es el que más se ve afectado por el tipo de sus argumentos.
Cuando se emplea con uno o dos operandos de tipo double, el operador de división se comporta como se debería
esperar. En cambio, cuando se utiliza con dos operando de tipo int, dicho operador produce la parte entera del cociente. En
otras palabras, la división entera desecha la parte que sigue al punto decimal. Así pues, 10/3 es 3 (no 3.333...), 5/2 es 2 (no
2.5). Observe que el número no se redondea; la parte que sigue al punto decimal se desecha, por más grande que sea.
Podemos emplear el operador % con operandos de tipo int para recuperar la información que se pierde cuando
usamos / para dividir números de tipo int. Cuando se emplean con valores de tipo int, los dos operadores / y % producen los
dos números que se obtienen cuando se ejecuta el algoritmo de división larga que se aprende en la primaria. Por ejemplo, 17
dividido entre 5 da 3 con un residuo de 2. La operación / produce el número de veces que un número “cabe” en otro. La
operación % da el residuo.
Incremento y Decremento:
C contiene dos operadores muy útiles que no existen generalmente en otros lenguajes de computadora. Son el de
incremento, añade 1 a su operando, y el decremento, resta 1 a su operando. En otras palabras:
x = x + 1; es lo mismo que x++;
y
x = x - 1; es lo mismo que x—;
Estos operadores pueden preceder o seguir al operando la diferencia es que cuando lo precede, C lleva a cabo la
operación antes de utilizar su valor. Si el operador sigue al operando, C utiliza su valor antes de realizar la operación. Por
ejemplo:
x = 10;
y= ++x;
Valor de y: 11
x = 10;
y= x++;
Valor de y: 10
La mayoría de los compiladores de C generan un código objeto muy rápido y eficiente para las operaciones de
incremento y decremento, siendo mejor que el que se obtendría usando la sentencia de asignación correspondiente. Por esta
razón, se debe usar estos operadores siempre que sea posible.
La precedencia de los operadores aritméticos es de mayor a menor como se muestra a continuación:
Mayor: ( )
++ --
* / %
Menor: + -
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
Los operadores del mismo nivel de precedencia son evaluados por el compilador de izquierda a derecha. Por
supuesto, se pueden utilizar paréntesis para alterar el orden de evaluación. Los paréntesis en C son tratados prácticamente
igual que en otros lenguajes. Los paréntesis fuerzan a que una operación, o un conjunto de las mismas, tenga un nivel de
precedencia mayor.
Relacionales y Lógicos:
En el término operador relacional la palabra relacional se refiere a la relación entre unos valores y otros. En el
término operador lógico la palabra lógico se refiere a las formas en que esas relaciones pueden conectarse entre sí. Los
discutiremos a la vez, ya que a menudo los operadores relacionales y lógicos actúan juntos.
La siguiente tabla muestra los operadores relacionales y lógicos.
Operadores Relacionales Operadores Lógicos
Operador Acción Operador Valor
> Mayor que && Y
>= Mayor o igual que || O
< Menor que ! No
<= Menor o igual que
== Igual
!= Diferente
La clave de los conceptos de operadores relacionales y lógicos es la idea de cierto (true) y falso (false). En C
cualquier valor distinto de 0 es cierto y el valor 0 es falso.
A continuación se muestra la tabla de verdad para los operadores lógicos usando 0 y 1
A B A&&B A||B !A !B 0 0 0 0 1 1
Falso Falso Falso Falso Cierto Cierto 0 1 0 1 1 0 1 1 1 1 0 0 1 0 0 1 0 1
Todos los operadores relacionales como los lógicos tienen un nivel de precedencia menor que los operadores
aritméticos. Esto significa que una expresión como 10>1+12 se evalúa como si se hubiera escrito 10>(1+12). El resultado,
por supuesto es falso.
Es posible combinar varias operaciones en una expresión tal como se muestra:
10>5&&!(10<9)||3<=4
La precedencia relativa entre los operadores relacionales y lógicos es la siguiente:
Mayor: ! > > = < <= == != && Menor: ||
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
A nivel de bit:
Al contrario que muchos otros lenguajes, el C soporta un completo juego de operadores a nivel de bits. Dado que el
lenguaje C se diseño para sustituir al lenguaje ensamblador en muchas tareas de programación, era importante permitir todas
las operaciones que se pueden hacer en ensamblador. Las operaciones a nivel de bits se refieren a la comprobación,
asignación o desplazamiento de los bits reales que componen un byte o una palabra, que corresponden a los tipos estándar
de C: char e int con sus variantes. Las operaciones a nivel de bits no se pueden usar sobre float, double, long double, void u
otros tipos mas complejos. La siguiente tabla lista los operadores que se aplican a las operaciones a nivel de bits. Estas
operaciones se aplican a los bits individuales de los operandos.
Operador Acción
& Y
| O
∧ O exclusiva (XOR)
~ Complemento a uno (NOT)
>> Desplazamiento a la derecha
<< Desplazamiento a la izquierda
Los operadores Y, O y No a nivel de bits se rigen con la misma tabla de verdad que sus equivalentes lógicos,
excepto que trabajan bit a bit. A continuación la tabla de verdad que se aplica a la O ( ^ ) exclusiva.
A B A^B
0 0 0
0 1 1
1 1 0
1 0 1
Las operaciones sobre bits son frecuentes en aplicaciones de controladores de dispositivos (como programas de
módem, rutinas de archivos de disco y rutinas de impresora) debido a que permiten enmascarar ciertos bits, como el de
paridad.
Las operaciones de desplazamiento de bits pueden ser muy útiles cuando se decodifica la entrada a través de
dispositivos externos. Los operadores de desplazamiento también pueden utilizarse para llevar a cabo operaciones muy
rápidas de multiplicación y división de enteros. Un desplazamiento a la izquierda equivale a una multiplicación por 2 y un
desplazamiento a la derecha a una división por 2.
Como lo indica la tabla, el resultado de
una XOR es verdadero sólo si
exactamente uno de los operandos es
cierto, de otro modo, es falso.
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
Más operadores:
Operador Condicional:
Este operador puede usarse para sustituir ciertas sentencias de la forma if-else con la forma general:
if(condición)
Expresión;
else
Expresión;
El operador condicional ? es un operador ternario, ya que necesita tres operandos y tiene la forma general:
Exp1?exp2 : exp3
El valor de una expresión con ? se determina de esta forma: se evalúa Exp1. Si es cierta se evalúa Exp2 y se
convierte en el valor de la expresión completa. Si Exp1 es falsa, se evalúa Exp3 y se convierte en el valor de la expresión
completa. Por ejemplo:
x = 10;
y = x > 9 ? 100 : 200;
En este ejemplo a y se le asigna el valor de 100. Si x hubiera sido menor que 9, habría recibido el valor de 200.
Utilizando la sentencia if-else, al mismo código es:
x = 10;
if(x>9) y = 100;
else y = 200;
Los operadores de puntero & y *
Un puntero es la dirección de memoria de una variable. Una variable puntero es una variable específicamente
declarada para contener un puntero a su tipo específico. El conocer la dirección de una variable puede ser de gran ayuda en
ciertos tipos de rutinas. Sin embargo, en C los punteros tiene tres funciones básicas.
1. Pueden proporcionar una rápida forma de referenciar los elementos de un arreglo.
2. Permiten a las funciones de C modificar los parámetros de llamada.
3. Dan soporte a las listas enlazadas y a otras estructuras de datos dinámicas.
Mas a delante definiremos ampliamente los Punteros, por el momento mencionaremos brevemente los operadores que nos
ayudaran a manipularlos.
El primer operador de punteros es &, un operador monario (que solo requiere un operando) que devuelve la
dirección de memoria del operando.
Nota: La precedencia de ? y : es menor
que la de cualquier otro operador
visto anteriormente.
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
Por ejemplo:
m=&muestra; coloca en m la dirección de memoria de muestra.
Esta es la dirección de la posición interna en la computadora de la variable. Recordar que no tiene nada que ver con
el valor de muestra.
Piense en esta forma; “m recibe la dirección de muestra”
El segundo operador de punteros es *, que es el complemento de &. Es también un operador monario que devuelve
el valor de la variable ubicada en la dirección que se especifica. Por ejemplo, si m contiene la dirección de memoria de la
variable muestra, entonces:
q= *m; Colocara el valor de muestra en q.
Piense en esta forma; “q recibe el valor que esta en la dirección m”
Por desgracia, el signo de la operación Y y el de “la dirección de” son el mismo, así como el de multiplicación y el
de “en la dirección”. Estos operadores no tienen relación entre sí.
Operador Serie:
La coma como operador encadena varias expresiones. La parte izquierda del operador como siempre se evalúa
como void. Esto significa que la expresión de la parte derecha se convierte en el valor de la expresión total separada por
coma, por ejemplo:
x= (y=3, y+1);
Primero asigna el valor 3 a y y luego asigna el valor 4 a x. Los paréntesis son necesarios debido a que este
operador tiene menor precedencia que el operador de asignación.
De igual forma nos permite combinar dos o mas expresiones en una sola. Por ejemplo:
x++, y++; Equivale a: x++;
y++;
Operadores punto (.) y flecha (->)
Estos operadores referencian elementos individuales de las estructuras y de las uniones. Las estructuras y las
uniones son tipos de datos compuestos que se pueden referenciar bajo un solo nombre.
El operador punto se usa cuando se trabaja realmente con la estructura o la unión. El operador flecha se usa
cuando se usa un puntero a una estructura o una unión.
Los paréntesis y los corchetes como operadores
Los paréntesis son operadores que aumentan la precedencia de las operaciones que contienen.
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
Los corchetes llevan a cabo el indexamiento de arreglos. Dado un arreglo, la expresión entre corchetes proporciona
un índice para el mismo.
Operador de Asignación:
En C se puede usar el operador de asignación en cualquier expresión válida. Este no es el caso de la mayoría de
los lenguajes de computadora que tratan el operador de asignación como un caso especial de sentencia. La forma general
del operador de asignación es:
Nombre_de_variable = expresión;
Donde la expresión puede ser tan simple como una constante o tan compleja como se requiera.
Nota: el destino, o parte izquierda de la asignación debe ser una variable o un puntero, no una función ni una constante.
A demás de este operador existen otros operadores de asignación relativa. Por ejemplo el operador += Por
ejemplo:
valor+=100; equivale a valor = valor + 100;
Resumen de precedencias:
Las precedencias entre todos los operadores del lenguaje C se muestra en la siguiente tabla.
Prioridad Operadores Asociatividad Mayor [ ] ( ) . -> Izquierda – Derecha ++ -- ! & * ~ Derecha – Izquierda * / % Izquierda – Derecha + - Izquierda – Derecha << >> Izquierda – Derecha < <= > >= Izquierda – Derecha == != Izquierda – Derecha & Izquierda – Derecha ∧ Izquierda – Derecha | Izquierda – Derecha && Izquierda – Derecha || Izquierda – Derecha ? : Derecha – Izquierda = *= /= %= -= >>=
<<= &= |= ∧= Derecha – Izquierda
Menor: Operador , Izquierda – Derecha
Tome en cuenta que:
• Si dos operadores se aplican al mismo operando, el operador con mayor prioridad se aplica al primero.
• Todos los operadores del mismo grupo tienen la misma prioridad y asociatividad.
• La asociatividad izquierda - derecha significa aplicar el operador más a la izquierda primero, y en la asociatividad derecha -
izquierda se aplica primero el operador más a la derecha.
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
Declaración de Variables y constantes.
En C++ existen cuatro tipos de constantes: Literales, Definidas, Declaradas y Enumeradas.
Constantes literales: o constantes en general, se clasifican en cuatro grupos: enteras, de coma flotante, caracteres y de
cadena.
Constantes Enteras: Pueden estar en distintos formatos:
Formato decimal: 925
Formato octal: 01635 Están precedidas de la cifra 0
Formato hexadecimal: 0X39D Están precedidas de 0X
Se pueden utilizar los sufijos L que significa long o bien U que significa unsigned.
123U 205L 398UL
Constantes de Coma flotante: también llamadas constantes reales.
95.236 0.36 .95 85. 9.35E5 987.e+3
Constantes caracteres: es un carácter del código ASCII encerado entre comillas simples:
‘a’ ‘B’ ‘+’
Constantes cadena: Es una secuencia de caracteres encerrados entre comillas dobles:
“La niña tiene un vestido rojo”
Se puede escribir una cadena en varias líneas, terminando cada línea con “\”
“Este es un ejemplo \
de una cadena en varias\
líneas”
Constantes de carácter de barra invertida.
Al incluir entre comillas simples las constantes carácter es suficiente para la mayoría de los caracteres imprimibles,
pero algunos, como el retorno de carro, son imposibles de introducir desde el teclado. Por esta razón, C incluye las
constantes especiales de carácter con barra invertida. Estos códigos se usan de la misma forma que cualquier otro
carácter.
Código Significado Código Significado
/b Espacio atrás. /0 Nulo.
/f Salto de página. // Barra invertida.
/n Salto de línea. /v Tabulación vertical.
/r Retorno de carro. /a Alerta.
/t Tabulación horizontal. /o Constante Octal.
/” Comillas dobles. /x Constante Hexadecimal.
/’ Comillas simples.
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
Constantes definidas:
Estas constantes pueden recibir nombres simbólicos mediante la directiva #define. C++ sustituirá los valores al
encontrarse dichas constantes. Ejemplo:
#define PI 3.1416
#define Tab ‘\t’
Constantes declaradas:
El cualificador const permite dar nombres simbólicos a constantes. Su formato general es:
const tipo nombre_identificador = valor;
Constantes enumeradas:
Estas constantes permiten crear listas de elementos afines. Por ejemplo:
enum frutas {manzana, pera, plátano, cereza, papaya}
Cuando dicha sentencia es procesada, el compilador asigna un valor que comienza en 0 a cada uno de los
elementos enumerados. Después de declarar un tipo de dato enumerado, se pueden crear variables de ese tipo. Por
ejemplo se puede definir una variable de tipo frutas:
frutas fruta_preferida=manzana;
Declaración de Variables:
C++ nos exige declarar y definir todas las variables antes de ser usadas. Dicha declaración deberá indicar el tipo de
la variable y su nombre. Si esta declaración aparta memoria para la variable, entonces estaremos hablando de una
definición.
La declaración de variables permite separar por comas una lista de variables, así como también asignarles un valor.
Ejemplo:
int secuencia;
char letra, codigo;
float cantidad=25.50;
int valor=1, dato=5;
Errores más comunes.
• La conversión de un tipo de datos más alto en la jerarquía de promoción a un tipo menor puede cambiar su valor.
• Utilizar el operador de incremento o decremento en una expresión que no sea nombre de variable simple, por ejemplo: ++(x+1), es
un error de sintaxis.
• Aunque 3<x<7 es una condición matemáticamente correcta, en C++ no se evalúa correctamente. Para obtener en C++ la
evaluación correcta, utilice (3<x && x<7)
ANSI: El instituto Estadounidense de Estándares Nacionales, es una organización de científicos y representantes de la industria que nombran comités encargados de crear estándares para
lenguajes de programación y otras cosas. En 1989 se estableció el comité ANSI para estandarizar C++. En 1991, ISO, la organización Internacional de Estándares (un cuerpo similar a
ANSI) se unió al proyecto ANSI de C++. En noviembre de 1997 el comité unido aprobó el estándar de C++.
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
Bibliotecas estándar de C y C++.
La biblioteca estándar de C contiene el código objeto de las funciones proporcionadas con el compilador. Para la
mayor parte de los compiladores de C la biblioteca estándar está contenida en un archivo. Sin embargo, algunas
implementaciones han agrupado las funciones relacionadas en sus propias bibliotecas por razones de eficiencia o
restricciones de tamaño.
El C del estándar ANSI tiene definido el contenido y la forma de la biblioteca estándar de C. Sin embargo, muchos
compiladores incorporan funciones que no están especificadas por el estándar. Por ejemplo, es muy común encontrar
funciones gráficas, rutinas de manejo de ratón además de las normales. Mientras que no transporte los programas que
escriba a un nuevo entorno, es perfectamente válido utilizar esas funciones no estándar. Sin embargo, si su código debe ser
portable, debe restringir su uso.
Aunque las bibliotecas son parecidas a los archivos objeto, hay una diferencia crucial: no todo el código de una
biblioteca se añade a su programa. Cuando enlaza un programa que consiste en varios archivos objeto, todo el código de
cada archivo objeto se convierte en parte del programa ejecutable final. Esto ocurre se esté o no utilizando el código. En
otras palabras, todos los archivos objeto especificados en tiempo de enlace se unen para formar el programa. Sin embargo,
este no es el caso de los archivos de biblioteca.
Una biblioteca es una colección de funciones. A diferencia de un archivo objeto, un archivo de biblioteca guarda el
nombre de cada función, los códigos objeto de las funciones y la información de re-ubicación necesaria para el proceso de
enlace. Cuando su programa se refiera a una función contenida en una biblioteca, el enlazador toma esa función y añade el
código objeto a su programa. De esta forma, sólo se añadirán al archivo ejecutable aquellas funciones que realmente se
utilicen en el programa.
Como las funciones se añaden de forma selectiva al programa cuando se utiliza una biblioteca, las funciones
estándar de C están contenidas en bibliotecas en lugar de archivos objetos.
Archivos de cabecera:
Muchas funciones que se encuentran en la biblioteca estándar trabajan con sus propios tipos de datos y variables a
las que su programa debe acceder. Estas variables y tipos están definidos en los archivos de cabecera, proporcionados junto
con el compilador, y deben incluirse (utilizando #include) en cualquier archivo que utilice funciones especificas que hagan
referencias a ellas.
Por ejemplo, para utilizar la función de raíz cuadrada, sqrt(valor) es necesario incluir el archivo de cabecera como
#include “math.h”
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
En la siguiente tabla se muestran los archivos de cabecera definidos por el estándar ANSI.
Archivo de Cabecera Propósito
ALLOC.H Funciones de asignación dinámica.
ASSERT.H Define la macro assert( ).
BCD.H Define la clase bcd (C++).
BIOS.H Funciones del ROM-BIOS.
COMPLEX.H Define la clase número complejo (C++).
CONIO.H Funciones de manejo de pantalla.
CTYPE.H Funciones de manejo de caracteres.
DIR.H Funciones de manejo de directorios.
DOS.H Funciones de interfaz con el DOS.
ERRNO.H Define códigos de error.
FCNTL.H Define constantes usadas por la función open( ).
FLOAT.H Define valores en coma flotante dependientes de la implementación.
FSTREAM.H Definiciones de clases de E/S de archivos (C++).
GENERIC.H Macros para falsificar declaraciones de clase genérica (C++).
GRAPHICS.H Funciones de gráficos.
IO.H Rutinas de E/S de tipo UNIX.
IOMANIP.H Define manipuladores de E/S (C++).
IOSTREAM.H Define clase flujo de E/S (C++).
LIMITS.H Define varios límites dependientes de la implementación.
LOCALE.H Funciones de especificación de país e idioma.
MATH.H Definiciones para la biblioteca matemática.
MEM.H Funciones de manipulación de memoria.
PROCESS.H Funciones spawn( ) y exec( ).
SETJMP.H Soporte a saltos no locales.
SHARE.H Compartición de archivos.
SIGNAL.H Define valores de señalización.
STDARG.H Soporte a listas de argumentos de longitud variable.
STDDEF.H Define algunas constantes de uso común.
STDIO.H Definiciones y declaraciones para secuencias de E/S estándar.
STDLIB.H Declaraciones variadas.
STREAM.H Definiciones para la antigua clase de flujo (C++).
STRSTREA.H Definiciones de clase Instrstream y ostrstream (C++).
STRING.H Soporte a las funciones cadena.
SYS\STAT.H Define constantes para la apertura de archivos.
SYS\TIMEB.H Declaraciones necesarias para la función ftime( ).
SYS\TYPES.H Declaraciones de tipo usadas con las funciones de tiempo.
TIME.H Soporte a las funciones de tiempo del sistema.
VALUES.H Constantes dependientes de la máquina.
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
Sentencias de Entrada y salida.
C es prácticamente único en su tratamiento de las operaciones de entrada/salida. Esto se debe a que el lenguaje C
no define ninguna palabra clave para realizar la E/S. Por el contrario, la entrada y salida se realizan a través de funciones de
biblioteca.
Las funciones más simples de E/S son getchar(), que lee un carácter del teclado y putchar(), que imprime un
carácter en la pantalla.
La función getchar() espera hasta que se pulsa una tecla y entonces devuelve su valor. La tecla pulsada aparece
automáticamente en la pantalla. La función putchar() escribe su argumento de carácter en la pantalla en la posición actual
del cursor. Los prototipos de estas funciones son:
int getchar(void);
int putchar(int c);
El archivo de cabecera requerido para estas funciones es STDIO.H
Existen algunos problemas potenciales con getchar(). El ANSI ha definido getchar() de forma que sea compatible
con la versión original de C basada en UNIX. En su forma original esta función guarda lo que llega hasta que se pulsa
ENTER (esto se debe a que los terminales de línea de los sistemas UNIX originales guardaban en un buffer la entrada y
había que pulsar ENTER para que la entrada fuera enviada a la computadora). Esto hace que se queden uno o más
caracteres esperando en la cola de entrada una vez que getchar() vuelve, lo cual es problemático en entornos interactivos.
Alternativas:
Dos de las funciones alternativas son getch() y getche(), que tienen estos prototipos:
int getch(void);
int getche(void);
La función getch() espera que se pulse una tecla e inmediatamente después devuelve el valor, pero no muestra el
carácter en la pantalla.
La función getche() trabaja de la misma forma que la anterior la diferencia es que si muestra el carácter en la
pantalla.
El archivo de cabecera requerido es CONIO.H
Escritura y lectura de cadenas:
El siguiente paso en la E/S de consola, en términos de complejidad y potencia, son las funciones gets() y puts().
Estas permiten leer y escribir cadenas de caracteres por la consola.
La función gets() lee una cadena de caracteres introducida por el teclado y la sitúa en la dirección apuntada por su
argumento de tipo puntero a carácter. Se pueden escribir caracteres por el teclado hasta pulsar un salto de carro. El salto de
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
carro no formará parte de la cadena; en su lugar, un terminador nulo es situado al final y después gets() termina. De hecho,
no se puede utilizar gets() para devolver un salto de carro (aunque getchar() puede hacerlo). Se pueden corregir errores de
escritura utilizando la tecla de retroceso antes de pulsar INTRO. El prototipo de esta función es:
char *gets(char *cadena);
Donde cadena es un array de caracteres donde se colocan los caracteres tecleados por el usuario. gets() también
devuelve un puntero a cadena. El prototipo de esta función se encuentra en STDIO.H.
Para la función puts() escribe su argumento de tipo cadena en la pantalla seguido de un carácter de salto de línea.
Su prototipo es:
int puts(char *s);
Una llamada a puts() requiere mucho menos tiempo que la misma llamada a printf(), porque puts() sólo puede
imprimir una cadena de caracteres, no puede imprimir números o hacer conversiones de formato. Por esto, requiere menos
espacio y se ejecuta más rápido. Esta función se utiliza a menudo cuando es importante tener un código muy optimizado. Es
importante también saber que devuelve EOF si ocurre algún error. Si no devuelve un valor distinto de cero. Sin embargo,
cuando se está escribiendo en la consola, se puede asumir que no va a ocurrir ningún error.
Función: Operación: getchar() Lee un carácter del teclado, espera un salto de carro. getche() Lee un carácter con eco. No espera un salto de carro. No está definida por el ANSI, pero es
una extensión común. getch() Lee un carácter sin eco. No espera un salto de carro. No está definida por el ANSI, pero es
una extensión común. putchar() Escribe un carácter en la pantalla. gets() Lee una cadena del teclado. puts() Escribe una cadena en la pantalla.
E/S por consola con formato.
Las funciones printf() y scanf() realizan la entrada y salida con formato. La función printf() escribe datos en la
consola y la función scanf(), su complementaria, lee datos desde el teclado. Ambas funciones pueden operar sobre
cualquiera de los tipos de datos existentes, incluyendo caracteres, cadenas y números.
Función printf()
El prototipo de esta función se encuentra en STDIO.H. Esta función devuelve el número de caracteres escritos o
bien un valor negativo si produce error.
int printf (char * cadena_de_control,lista_argumentos);
La cadena de Control esta formada por dos tipos de elementos, el primer elemento es el carácter que se mostrará en
pantalla. El segundo elemento contiene especificadores de formato que definen la forma en que se muestran los argumentos
posteriores. El especificador de formato empieza con un % y va seguido por el código del formato. Debe haber exactamente
el mismo número de argumentos que especificadores de formato y ambos deben coincidir en su orden de aparición de
izquierda a derecha. Por ejemplo:
printf(“El %c es muy %s”,’C’,”facil”);
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
La siguiente tabla muestra los especificadores de formato que acepta esta función.
Código Formato %c Carácter. %d Enteros decimales con signo. %i Enteros decimales con signo. %e Notación Científica (e minúscula). %E Notación Científica (E mayúscula). %f Coma Flotante. %g Usar %e o %f, el más corto. %G Usar %E o %F, el más corto. %o Octal sin signo. %s Cadena de caracteres. %u Enteros decimales sin signo. %x Hexadecimales sin signo (letras minúsculas). %X Hexadecimales sin signo (letras mayúsculas). %p Mostrar puntero. %n El argumento asociado es un puntero a entero al que se asigna el numero
de caracteres escritos. %% Imprimir el signo %.
Notas Importantes:
• Para imprimir un solo carácter se utiliza %c, para una cadena se utiliza %s.
• Los especificadores %d y %i son equivalentes.
• Para imprimir una dirección de memoria utilice %p.
El especificador de formato %n es diferente al resto. En lugar de hacer que printf() imprima algo, produce en
printf() el efecto de cargar la variable apuntada por su correspondiente argumento con un valor igual al número de caracteres
que han sido impresos. En otras palabras, el valor que corresponde al especificador de formato %n debe ser un puntero a
una variable.
Después de terminada la ejecución de printf() esa variable tendrá el número de caracteres que se han impreso
hasta el punto en el cual se encontró %n.
Para alterar el numero de dígitos a imprimir en los formatos entero y flotante se pone el numero de lugares que
necesite entre el signo de porciento y el especificador de formato. (%05d muestra 00123). Este formato se utiliza
principalmente para crear tablas alineadas. De igual forma este formato se utiliza para especificar las posiciones decimales a
imprimir.
Función scanf()
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
Es la rutina de entrada por consola de propósito general. Puede leer todos los tipos de datos que suministra el
compilador y convierte los números automáticamente al formato interno apropiado. Su prototipo se encuentra en STDIO.H y
este es:
int scanf(char *cadena _de control, lista de argumentos)
Esta función devuelve el número de datos a los que se ha asignado un valor con éxito. Si se produce un error
devuelve EOF. La cadena de control determina cómo se leen los valores en las variables a las que se hace referencia en la
lista de argumentos.
La siguiente tabla muestra los especificadores de formato que acepta esta función.
Código Formato %c Leer un único carácter. %d Leer un entero decimal. %i Leer un entero decimal. %e Leer un número de tipo coma flotante. %f Leer un número de tipo coma flotante. %g Leer un número de tipo coma flotante. %o Leer un número octal. %s Leer una cadena. %x Leer un número hexadecimal. %p Leer un puntero. %n Recibe un valor entero igual al número de caracteres leídos. %u Leer un entero sin signo. %[ ] Muestrea un conjunto de caracteres.
Estructuras de Control:
El estándar ANSI clasifica las sentencias de C en los siguientes grupos:
De selección.
De iteración.
De salto.
De etiquetado.
De expresión.
De bloque.
Entre las sentencias de selección se incluyen if y switch (a menudo se usa “sentencia condicional” en lugar de
“sentencia de selección”. Sin embargo, el estándar ANSI utiliza selección). Las sentencias de iteración son while, for y do
while. También se conocen normalmente como sentencias de bucles. Las sentencias de salto son break, continue, goto y
return. Las sentencias de etiquetado son case, default y label. Las sentencias de expresión son las compuestas por
expresiones válidas en C. Las sentencias de bloque son simplemente bloques de código.
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
Muchas sentencias de C se basan en una prueba condicional que determinan la acción que se ha de llevar a cabo.
Una expresión condicional tiene como resultado un valor cierto o falso. A diferencia de muchos lenguajes cualquier valor
distinto de cero es cierto, incluyendo los números negativos. El cero es el único valor falso.
Sentencias de Selección:
Como se menciono anteriormente C soporta las sentencias if y switch. Además, el operador ? puede ser utilizado
para ciertas situaciones.
If:
Su forma general es: Representación Gráfica:
if (expresión) sentencia;
else sentencia;
Donde sentencia puede ser una sentencia simple, un bloque de sentencias o nada (en el caso de sentencias
vacías). La cláusula else es opcional.
Si la expresión de if es cierta, se ejecuta la sentencia o el bloque de sentencias que constituye el objeto del if. En
cualquier otro caso se ejecuta la sentencia o bloque de sentencias que constituye el objetivo del else, si existe. Recuerde que
sólo se ejecuta el código asociado al if o al else, nunca ambos.
La sentencia condicional que controla el if debe producir un resultado escalar. Un escalar es cualquiera de los tipos
entero, de carácter o de coma flotante. Sin embargo, es raro usar un número en coma flotante para controlar una sentencia
condicional, ya que disminuye la velocidad de ejecución considerablemente.
Ifs anidados.
Un if anidado es un if que es el objeto de otro if o else. Los ifs anidados son muy comunes en programación. En c
una sentencia else siempre se refiere al if más próximo que esté en el mismo bloque que el else y que no esté ya asociado a
un if.
Por Ejemplo.
If (i) {
If (j) sentencia 1;
Condición
Sentencia o Bloque de Sentencias
Sentencia o Bloque de Sentencias
Sentencias
V F
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
If (k) sentencia 2; Else sentencia 3;
}
else sentencia 4;
If-else-if
Una construcción común en programación es la escala if-else-if. Su forma general es:
If (expresión) sentencia; Else
If (expresión) sentencia;
Else If (expresión) sentencia;
.
.
.
Else sentencia;
Las condiciones se evalúan desde arriba hasta abajo. Tan pronto como se encuentre una condición cierta, se
ejecuta la sentencia asociada con ella y se pasa por alto el resto de la escala. Si ninguna de las condiciones es cierta, se
ejecuta el else final. Es decir, si todas las pruebas condicionales fallan, se ejecuta la última sentencia else. Si no hay else
final, no tiene lugar ninguna acción si todas las condiciones son falsas.
Switch
Esta sentencia de selección múltiple compara sucesivamente el valor de una expresión con una lista de constantes
enteras o de caracteres. Cuando se encuentra una correspondencia, se ejecutan las sentencias asociadas con la constante.
Su forma general es: Su representación gráfica es:
switch (expresión) {
case constante 1: secuencia de sentencias;
break;
case constante 2: secuencia de sentencias;
break;
.
.
.
. case constante n:
secuencia de sentencias;
break; default:
secuencia de sentencias;
}
Variable
Secuencia de Sentencias
Secuencia de Sentencias
Secuencia de Sentencias
Secuencia de Sentencias
. . ..
Constante 1
Constante 2 Constante n
default
Sentencias
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
Se comprueba el valor de la expresión, por orden, con los valores de las constantes especificadas en las sentencias
case. Cuando se encuentra una correspondencia, se ejecuta la secuencia de sentencias asociadas con ese case, hasta que
se encuentra la sentencia break. La sentencia default se ejecuta si no se ha encontrado ninguna correspondencia. Esta
ultima sentencia es opcional y si no esta presente no se ejecuta ninguna acción al fallar todas las comparaciones.
El estándar ANSI especifica que una sentencia switch debe poder tener al menos 257 sentencias case. En la
practica, se limita el número de estas sentencias a muchas menos por razones de eficiencia. Cabe aclarar que la sentencia
case, aunque es una sentencia de etiquetado, no tiene efecto fuera del switch.
La sentencia break es una de las sentencias de salto del lenguaje C. Cuando se encuentra una de estas sentencias
dentro de una sentencia switch, la ejecución del programa salta a la línea de código que sigue a dicha sentencia switch.
Importante:
• La sentencia switch se diferencia de la sentencia if en que switch sólo puede comprobar la igualdad, mientras if puede evaluar
expresiones relacionales o lógicas.
• No puede haber dos constantes case en el mismo switch que tengan los mismos valores. Por supuesto, una sentencia switch
puede estar contenida dentro de otra.
• Si se utilizan constantes de tipo carácter en la sentencia switch, se convierten automáticamente en valores enteros.
Switch anidados:
Se puede tener un switch formando parte de la secuencia de sentencia de otro switch. Incluso si las constantes
case del switch interior y del exterior contienen valores comunes, no provocara ninguna dificultad.
Ejemplo:
switch (expresión) { case constante 1:
switch (expresión) {
case constante 1: .
.
. }
break; case constante 2:
secuencia de sentencias; break; . . .
case constante n: secuencia de sentencias; break;
default: secuencia de sentencias; }
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
El operador condicional ?:
Se puede usar este operador para reemplazar las sentencias if-else con la forma general:
if (condición)
Expresión
else
Expresión
Sin embargo, el objetivo de if y else debe ser una expresión simple, no otra sentencia de C.
La ? es un operador ternario, ya que necesita tres operandos y tiene la forma general:
Exp1? Exp2: Exp3
Donde Exp1, Exp2 y Exp3 son expresiones. Observe el uso y la posición de los dos puntos.
El valor de una expresión con ? se determina de esta forma: se evalúa Exp1. Si es cierta, se evalúa Exp2 y se
convierte en el valor de la expresión completa. Si Exp1 es falsa, se evalúa Exp3 y su valor se convierte en el valor de la
expresión completa. Por ejemplo:
x = 10; x = 10;
y = x > 9 ? 100 : 200; if (x>9) y = 100;
else y = 200;
El uso del operador ? para sustituir las sentencias if-else no está limitado únicamente a sentencias de asignación.
Es posible utilizar funciones como parte de las expresiones que formen la parte verdadera y/o falsa del operador condicional.
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
Estatutos de ciclos: Iteración
Para empezar diremos que un ciclo (bucle, lazo, loop, etc.) es un segmento de un algoritmo o programa, cuyas
instrucciones se repiten un número determinado de veces mientras se cumple una determinada condición. Se debe
establecer un mecanismo para determinar las tareas repetitivas. Este mecanismo es una condición que puede ser verdadera
o falsa y que se comprueba una vez a cada paso o iteración del ciclo.
Un ciclo consta de tres partes:
1. Decisión,
2. Cuerpo del ciclo,
3. Salida del ciclo.
Ciclos en programas: while, for, do-while.
for:
El formato general del ciclo for de C se encuentra de una forma o de otra en todos los lenguajes de programación
procedimentales. Su forma general es:
for(inicialización ; Expresión_de_verificación ; Actualización) sentencia;
Representación gráfica:
Este bucle permite muchas variantes. Sin embargo, la inicialización normalmente es una sentencia de asignación
que se utiliza para iniciar la variable de control del bucle. La Expresión de verificación o condición es una expresión relacional
que determina cuando finaliza el bucle. La actualización define como cambia la variable de control cada vez que se repite el
bucle. Estas tres secciones principales deben estar separadas por punto y coma. El bucle for continua ejecutándose
mientras que la condición sea cierta. Una vez que la condición se hace falsa, la ejecución del programa continua por la
sentencia siguiente al for.
Bucle infinito:
Aunque se puede utilizar cualquier sentencia de bucle para crear un bucle infinito, es for el que se usa
tradicionalmente para este propósito. Como no se necesita ninguna de las tres expresiones que lo constituyen se puede
conseguir que el bucle no tenga fin dejando la expresión condicional vacía.
Inicialización
Expresión de verificación
Actualización
Secuencia de Sentencias
V
F
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
Es permitido poner expresiones de inicialización y de incremento, pero es más común entre los programadores de C
utilizar for(;;) para expresar un bucle infinito.
Realmente, dicha construcción de for no garantiza un bucle infinito, ya que la sentencia break, cuando se encuentra
en cualquier lugar dentro del cuerpo de un bucle da lugar a la terminación inmediata. El control del programa sigue en el
código que sigue al bucle.
while
Casi todos los programas incluyen alguna acción que se repite varias veces. Por ejemplo, un programa que calcula
la paga neta de un empleado. Si la compañía tiene 100 empleados, un programa de nómina más completo repetiría el
cálculo cien veces. Esto se puede realizar de diferentes formas y una de ella es utilizar el ciclo while.
Su forma general es:
while (condición) sentencia;
Representación Gráfica:
Donde sentencia puede ser una sentencia vacía, una sentencia simple o un bloque de sentencias que se
repiten mientras la condición sea verdadera. La condición puede ser cualquier expresión verdadera y cualquier
valor distinto de 0 es verdadero.
Al igual que el bucle for, el bucle while comprueba la condición al principio, lo que supone que el código del
mismo se ejecuta 0 o más veces.
Do-while:
Si sabemos que pase lo que pase el cuerpo del bucle while se debe ejecutar al menos una vez, usamos la
instrucción do-while. Esta instrucción a diferencia del while y for analiza la condición al final del bucle.
Su forma general es: Representación Gráfica:
do
{
bloque de sentencias
} while(condición);
Condición
Sentencia o bloque de sentencias
V
Sentencia o bloque de sentencias
F
Bloque de Sentencias
Condición
Bloque de Sentencias F
V
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
Ciclos de conteo y ciclos condicionales.
Ciclo de conteo.
También llamados de repetición controlada o definida, porque se sabe con anticipación y exactitud el
número de veces que se ha de repetir el ciclo. Para este tipo de ciclo se hace uso de una variable que controla el
número de veces que se va a repetir el ciclo. Esta variable de control debe estar cambiando (incrementándose o
decrementándose) cada vez que se ejecuta el grupo de sentencias del ciclo.
Cuando el valor de la variable de control indica que se ha ejecutado el número exacto de repeticiones, termina
el ciclo, y entonces se continua ejecutando la siguiente sentencia en el programa.
Ciclo condicional.
A veces llamado de repetición indefinida, porque no se sabe con anticipación el número de veces que se
ejecutará el ciclo. Es aquel en que se va a estar ejecutando el ciclo mientras que una condición se cumpla. La
mayoría de las veces este tipo de ciclos involucra enunciados que deben obtener datos cada vez que el ciclo se
ejecuta. Cuando la condición no se cumple, se ejecuta la siguiente sentencia en el programa.
Diseño de ciclos.
De acuerdo al problema que se plantea resolver, el programador tiene que: primero entender y analizar el
problema, luego determinar el tipo de acciones a realizar. Entonces, se verifica el tipo de ciclo que conviene utilizar
para la solución. Una vez identificado el tipo de ciclo, es decir, cual de las estructuras de ciclo es más conveniente
usar para la solución. Si el programador generó una o más soluciones entonces debe escoger aquella que sea la más
óptima posible.
Ciclos anidados.
En algunas aplicaciones se requieren utilizar ciclos simples, en otras aplicaciones se requiere utilizar
operaciones cíclicas dentro de ciclos. A esto se le llama ciclos anidados.
El estándar ANSI estipula que al menos se deben soportar 15 niveles de anidamiento (la mayoría de los
compiladores permiten más). Los ciclos anidados se utilizan para resolver una gran variedad de problemas de
programación.
Sentencias Break y Continue
Las Sentencias break y continue se pueden usar para modificar la ejecución de estructuras de control
predefinidas, como los ciclos, cuando se presentan ciertas condiciones. En general, la sentencia break se usa para
terminar un ciclo inmediatamente y la sentencia continue se usa para pasar por alto una iteración de un ciclo.
Break:
Esta sentencia tiene dos usos. Se puede usar para finalizar un case en una sentencia switch o se puede usar
para forzar la terminación inmediata de un bucle, saltando la evaluación condicional normal del ciclo.
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
Cuando se encuentra esta sentencia dentro de un bucle, el bucle termina inmediatamente y el control pasa a
la siguiente sentencia en el programa.
La sentencia break generalmente se utiliza como parte de la estructura if dentro de un ciclo para terminar la
estructura del mismo si ocurren ciertas condiciones. A esto le llamaremos forzar la terminación inmediata de un
ciclo.
Importante:
• La sentencia break normalmente se utiliza en ciclos donde una condición especial puede dar lugar a la terminación
inmediata.
• Un break utilizado en un switch o en un ciclo, solo afecta a ese switch o a ese ciclo en especial, tome en cuenta esto
cuando maneje switch o ciclos anidados.
• La principal función del break es el romper ciclos cuando existe el potencial para un ciclo infinito.
Continue:
La sentencia continue funciona de una forma similar al break. Sin embargo, en lugar de forzar la terminación,
continue fuerza una nueva iteración del ciclo y salta cualquier código que exista en forma intermedia.
Importante:
• Para los ciclo while y do-while, una sentencia continue hace que el control del programa pase a la prueba condicional y
continúe el proceso de iteración.
• Para el ciclo for, hace que se ejecute la parte de incremento del ciclo, seguida de la prueba condicional.
• Para ciclos anidados, el continue solo ignora la iteración del ciclo donde se encuentra.
Etiquetas y goto:
El uso del goto ha decaído desde hace algunos años, La idea principal que los programadores tienen sobre el
mismo es que tiende a hacer los programas ilegibles. Sin embargo, hay veces en las que el uso del goto realmente
clarifica el flujo del programa más que confundirlo.
Se indica que no hay situaciones de programación que necesiten de su uso; más bien, es conveniente y
puede ser beneficioso, si se usa apropiadamente, en ciertas situaciones de programación.
En un lenguaje como C, que tiene un rico conjunto de estructuras de control y permite un control adicional
usando break y continue, no es muy necesario.
La sentencia goto necesita de una etiqueta para operar. Una etiqueta es un identificador válido de C que se
sigue por dos puntos.
Depuración y prueba de programas.
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
Una equivocación en un programa es un bug (error de programación), y el proceso de eliminar tales errores
se llama depuración. Hay varias historias acerca del origen de la terminología. La más interesante es que los
términos se originaron en los primeros días de la computación, cuando el hardware era extremadamente sensible. La
contraalmirante Grace Murray Hopper (1906-1992) fue “la tercera programadora de la primera computadora digital a
gran escala en el mundo”. Cuando Hopper estaba trabajando con la computadora Mark I de Harvard, bajo las órdenes
del profesor Howard H. Aiken de Hardvar, cuando una polilla hizo que fallara un revelador. Hopper y los demás
programadores pegaron con cinta adhesiva el cadáver de la polilla a la bitácora, y anotaron en ésta: “Primer caso
comprobado de “bug” hallado”. La bitácora se exhibe actualmente en el Museo Naval de Dahlgren, Virginia. Éste
fue el primer “bug” de computadora documentado. El profesor Aiken solía visitar las instalaciones en momentos de
poca actividad y preguntar si se estaban calculando números. Los programadores contestaban que estaban “limpiando
de bichos” (debugging) la computadora. Hoy en día, un bug es un error en un programa.
Tipos de errores en los programas:
El compilador detecta ciertos tipos de errores y despliega un mensaje de error cuando encuentra una
equivocación. Además, detecta los llamados errores de sintaxis, ya que son en general, violaciones de la sintaxis
(reglas gramaticales) del lenguaje de programación, tal como omitir un punto y coma.
Si el compilador descubre que el programa contiene un error de sintaxis, indicará dónde es probable que
esté y qué tipo de error podría ser. Si el compilador dice que el programa contiene un error de sintaxis, podemos
estar seguros de que así es. Sin embargo, el compilador podría no estar en lo correcto acerca de la ubicación o la
naturaleza del error. El compilador es más hábil para determinar la ubicación de un error, dentro de un margen de una
o dos líneas, que para determinar la causa del error. La razón es que el compilador tiene que adivinar qué es lo que el
programador quiso escribir, y fácilmente podría errar al adivinar. Los mensajes de error subsecuentes al primero tienen
mayores posibilidades de ser incorrectos con respecto a la ubicación o a la naturaleza del error. Esto también se debe
a que el compilador tiene que adivinar nuestra intención. Si la primera conjetura del compilador fue incorrecta, esto
afectará su análisis de los errores futuros, ya que el análisis se basará en una suposición falsa.
Si nuestro programa contiene algo que viola directamente las reglas de sintaxis del lenguaje de programación,
el compilador producirá un mensaje de error. Sin embargo, hay ocasiones en que el compilador sólo genera un
mensaje de advertencia, lo que significa que hemos hecho algo que no es, en términos técnicos: una violación de las
reglas del sintaxis del lenguaje de programación, pero que es lo bastante inusitado como para indicar un probable
error. Cuando recibimos un mensaje de advertencia, el compilador nos está preguntando “¿Estás seguro de que
esto es lo que quieres?” Es recomendable tratar todas las advertencias como si fueran errores.
Hay ciertos tipos de errores que el sistema de computación sólo puede detectar cuando se ejecuta el
programa. Por ello, reciben el nombre de errores de tiempo de ejecución.
Casi todos los sistemas de computación detectan ciertos errores de este tipo y despliegan un mensaje de
error apropiado. Muchos errores de tiempo de ejecución tienen que ver con cálculos numéricos. Por ejemplo, si la
computadora intenta dividir un número entre cero, eso normalmente es un error de tiempo de ejecución.
��������������� �� ��� ��������������� ���
���������������� ����� � �� ��
El hecho de que el compilador apruebe el programa y éste se ejecute una vez sin mensajes de error de
tiempo de ejecución, no garantiza que el programa sea correcto. Recordemos que el compilador sólo nos indica si
hemos escrito un programa en C sintácticamente correcto. No nos dice si el programa hace lo que queremos que
haga. Las equivocaciones en el algoritmo subyacente o en la traducción del algoritmo al lenguaje se llaman errores de
lógica. Por ejemplo, si usáramos por equivocación el signo de suma + en lugar del signo de multiplicación * en un
programa donde tendríamos que calcular el Iva, entonces dicho programa tendría un error de lógica. El programa se
compilaría y ejecutaría sin contratiempos, pero daría una respuesta equivocada. Si el compilador aprobó nuestro
programa y no hay errores de tiempo de ejecución, pero el programa no funciona debidamente, sin duda contiene un
error de lógica. Estos errores son los mas difíciles de diagnosticar, porque la computadora no nos da mensajes de
error que nos ayuden a encontrarlos.
Una de las formas para detectar los errores de lógica o de tiempo de ejecución es la “Prueba de
escritorio”, ésta es un procedimiento similar a revisar una carta o manuscrito. La idea es seguir el programa en papel
para asegurarse que éste trabaja en forma lógica. Debe considerar varias posibilidades de entrada y escribir cualquier
resultado generado durante la ejecución del programa. En particular, tratar de determinar que hará el programa con
datos no muy comunes considerando posibilidades de entrada que no deberían pasar.
En resumen recordemos:
Una buena definición del problema y un buen planteamiento evitará errores en el programa. Sin embargo,
siempre habrá unas cuantas fallas que no son detectadas. Depurar el programa consume más tiempo en todo el
proceso de programación. Thomas Edison decía, “la Programación es un 10% de inspiración y un 90% de
depuración”. Todos los buenos programadores son buenos depuradores.
Para hacer más fácil el proceso de depuración le aconsejamos seguir los siguientes pasos:
1. Darse cuenta que tienen un error.
2. Localizar y determinar la causa del error.
3. Corregir el error.
Cuando se empieza a programar es muy común que emitan la fase de “prueba de escritorio” ¿Porque no
esperar a correr el programa una vez que lo haya escrito?. Se dará cuenta, conforme obtenga experiencia, que se
puede ahorrar tiempo con la prueba de escritorio.
• Contador: es una variable cuyo valor se incrementa o decrementa en una cantidad constante en cata iteración.
• Acumulador: es una variable cuya misión es almacenar cantidades variables resultantes de sumas sucesivas. Realiza la
misma función que un contador, con la diferencia de que el incremento o decremento de cada suma se variable en lugar de
constante, como en el caso del contador.