DOCUMENTACIÓN PARA EL TRABAJO CON LA PLATAFORMA GUADALBOT I.E.S VIRGEN DE LAS NIEVES Programación C para microcontroladores Tema 2. Tipos de datos, variables y operadores Índice de contenidos Tipos de datos.......................................................................................................................................2 Identificadores......................................................................................................................................2 Constantes.............................................................................................................................................3 Variables...............................................................................................................................................5 Especificadores de tipo.........................................................................................................................6 Congruencia y conversiones entre tipos. Operador cast()....................................................................7 Operadores............................................................................................................................................8 Jerarquía de operaciones. Precedencia de operadores........................................................................13 1 Tipos de datos En lenguaje ensamblador todos los datos son de 8 bits sin formato que ocupan registros de la RAM, pero en un lenguaje de alto nivel como el C estos registros son tratados de acuerdo con formatos que les permiten representar números de 8, 16, 32, e incluso más bits, con o sin signo y números enteros o fracionarios, vectores, matrices y punteros. Siendo este tipo de datos los que se denominan datos básicos, fundamentales o primitivos. Usando los modificadores de formato y cualquiera de los tipos primitivos podemos obtener los denominados datos derivados. Existen otros tipos de datos, basados en los básicos, denominados datos compuestos o complejos, como son las estructuras y las uniones, que veremos más adelante. Los tipos de datos básicos del lenguaje C son: Tipos básicos Tipo Tam. Rango Unsigned Signed Descripción int1 1 bit 0a1 No disponible int8 8 bit 0 a 255 int16 16 bit 0 a 65535 int32 32 bit float32 32 bit void ― Tipo por defecto Entero de 1 bit short int1 -128 a 127 Entero de 1 byte char unsigned int8 -32.768 a 32.767 Entero de 2 bytes int int8 long int16 0 a 4294967295 -2.147.483.648 a 2.147.483.647 Entero de 4 bytes - 1,5x10 +45 a 3,4x10 +38 ― Tipo ANSI C Coma flotante Ausencia de tipo (sin valor) long long int32 float float32 Todos los tipos, excepto float, son por defecto unsigned, sin embargo, podemos siempre poner unsigned o signed. Short y long pueden ir seguidos de la palabra clave INT sin que tenga efecto alguno. SHORT es un tipo especial que se utiliza para generar código muy eficiente para operaciones a nivel de bits y de entrada/salida. Podemos definir matrices de bits (INT1), sin embargo los punteros a bits no están permitidos. Los archivos de cabecera de los dispositivos tienen las directivas define para byte como int8 y BOOLEAN como int1. Identificadores Los tipos de datos vistos son identificadores que permiten lo que es básico en programación, que es diferenciar por su nombre distintos elementos de un programa. Podemos decir que un identificador es el nombre que se le da a un elemento de un programa. Los identificadores de tipos de datos están predefinidos, es decir, forman parte del lenguaje C. Pero lógicamente nosotros también podemos definir nuestros propios identificadores, como por ejemplo variables y constantes definidas por el programador. Las reglas sintácticas del lenguaje C para asignar identificadores son: 1. Debe constar de uno o más caracteres. 2. El primer carácter debe ser siempre una letra o el carácter de subrayado, mientras que, todos los demás pueden ser letras, dígitos o el carácter de subrayado. Las letras pueden ser mayúsculas o minúsculas pero del alfabeto inglés. No se permite el uso de caracteres especiales como la letra ñ, vocales acentuadas, etc. 3. No deben exitir dos identificadores iguales, es decir, dos elementos de un programa con el mismo nombre, lo que no indica que un identificador no pueda aparecer más de una vez en un programa. 2 Algunos identificadores válidos pueden ser: numero1 DiaMes dia_del_mes NOMBRE1 _ciudad x Mes mes Los dos últimos identificadores definidos son distintos ya que los identificadores son sensibles a minúsculas y mayúsculas. Algunos identificadores NO válidos pueden ser: 12345 //No comienza por letra o subrayado _Día //Acento no permitido numero* //Carácter no permitido dia del mes //Espacios no permitidos año //La ñ no está permitida int //Palabra reservada Por último indicaremos como aconsejable que los identificadores tengan un significado descriptivo o afín a lo que representan. En lenguaje C estándar las 32 palabras reservadas son las siguientes: auto double int struct break else long switch case enum register typedef char extern return union const float short unsigned continue for signed void default goto sizeof volatile do if static while Constantes Las constantes pueden ser de tipo entero, real, carácter, de cadena o enumerado, que estudiaremos en otro momento. Las constantes se pueden expresar bien por su valor (por ejemplo: -23 ó 15) o bien mediante un identificador. Para expresar una constante con un identificador debemos declararla previamente mediante la directiva al preprocesador #define, cuya sintaxis es: #define <nombre de la constante> <valor de la constante> #define PI 3.141592653 #define NUMERO_E 2,7182818284 Esta directiva indica al preprocesador que debe sustituir, en el código fuente del programa, todas las ocurrencias del nombre de la constante por el valor indicado antes de la compilación. Es importante observar que en la declaración de una constante con la directiva #define no se usa el punto y coma, por lo que cada constante se debe declarar en una línea diferente. Normalmente los identificadores de las constantes se escriben en mayúsculas para poder localizarlos facilmente en el programa. Existe en C una especie de constante declarada a partir de una variable (las estudiamos seguidamente) indicando que su valor es inalterable. Para ello, se utiliza el cualificador const. Por ejemplo: const int temperatura = 15; inicializa la variable temperatura con el valor 15 y, por medio de const, se indica que su valor no puede cambiar durante la ejecución del programa, lo que hace que en cierta manera, la variable temperatura está simulando a una constante. 3 Podemos especificar constantes usando sufijos y expresando su valor en decimal, octal, hexadecimal o binario, tal y como vemos seguidamente. Valor Expresando en Tipo Uso del sufijo 305 Decimal int8 305U o305 Octal (letra o delante del valor) long 30UL 0x305 Hexadecimal (0x) signed int16 55L 0b10010101 Binario (0b) float 3.1416F 'x' char 'Z' Carácter Una constante de tipo caracter es cualquier carácter encerrado entre comillas simples, como por ejemplo 'x' o 'Z'. El valor de una constante carácter es su valor numérico según el código ASCII. Ciertos caracteres como la comilla simple ('), la barra invertida o contrabarra (\) y otros, se representan mediante las denominadas secuencias de escape que vemos en la tabla siguiente. Estas secuencias de escape están constituidas por la barra invertida (\) seguida de otro carácter y su fin es cambiar el significado normal del carácter que sigue a la barra invertida. Si consultamos el documento "Funciones LCD_Caracteres LCD y ASCII" podemos extraer algunos de estos caracteres especiales como los que vemos a continuacón. Constante Valor ASCII Dec Oct Hex Lenguaje C Bin Significado bel 7 0o007 0x07 0b00000111 \a Sonido de alerta ht 9 0o011 0x09 0b00001001 \t Tabulación horizontal nl 10 0o012 0x0A 0b00001010 \n Salto de línea bs 8 0o010 0x08 0b00001000 \b Retroceso \ 92 0o134 0x5C 0b01011100 \\ Barra invertida o contrabarra ' 39 0o047 0x27 0b00100111 \' Comilla simple " 34 0o042 0x22 0b00100010 \" Comillas dobles nul 0 0o000 0x00 0b00000000 \0 Sin operación (carácter nulo) Un tipo de dato constante que requiere una exlicación son las cadenas de caracteres. Una cadena de caracteres es un conjunto de caracteres encerrados entre comillas dobles ("). Por ejemplo: "Este conjunto de caracteres es una cadena" Dentro de la cadena, podemos poner caracteres en blanco y secuencias de escape como las vistas. Si queremos que aparezcan las propias comillas en la cadena debemos anteponerle la contrabarra para evitar que las interprete como final de la cadena. Indicar como muy importante que el compilador adiciona siempre un byte nulo (\0) al final de cada cadena de caracteres para señalar el final de la misma. Esto solamente es importante de cara al tamaño ya que, por ejemplo, la cadena "ordenador" no ocupa 9 bytes, sino 10, los nueve de la cadena más el del carácter nulo. Algunos ejemplos de cadenas de caracteres pueden ser: "Programación" "'A'" " Esta cadena contiene espacios en blanco " "Esta cadena encierra \"otra cadena\" entre comillas y provoca un salto de línea.\n" 4 Otro tipo de dato constante es el denominado tipo enumerado (enum). Se trata de una clase especial de constantes de C que consisten en una lista de valores constantes de tipo int. Para crear una enumeración debemos definir un nuevo tipo de datos, denominado tipo enumerado y declarar una variable de este tipo. Su sintaxis es la siguiente: enum [<identificador de tipo_enumerado>] { <nombre de la constante> [ = <valor>], ... } [lista de variables]; El identificador de tipo es opcional y nos permitirá declarar más variables de tipo enumerado: [enum] <identificador de tipo_enumerado> <variable1> [,<variable2> [...] ]; La lista de variables es opcional y se puede definir separada por comas en una o en varias lineas. Vamos a ver un ejemplo típico de enumeración que es el de las horas que marca un reloj. enum TipoDeHoras { una=1, dos, tres, cuatro, cinco, seis, siete, ocho, nueve, diez, once, doce, trece=1, catorce, quince, dieciseis, diecisiete, dieciocho, diecinueve, veinte, veintiuna, veintidos, veintitres, veinticuatro=0 }; Vemos que las horas una y trece valen 1, dos y catorce valen 2, etc.; y que veinticuatro vale 0. Vemos en el ejemplo que una vez que se asigna un valor a un elemento de la lista los demás toman valores correlativos. En caso de no asignar ningún valor, el primer elemento toma el valor 0. Otro ejemplo típico de enumeración es el de los días de la semana, como vemos en el ejemplo siguiente. enum DiasSemana { lunes, martes, miercoles, jueves, viernes, sabado, domingo } hoy; //declara la variable hoy del tipo enumerado DiasSemana enum DiasSemana ayer; //declara la variable ayer del tipo enumerado DiasSemana ayer=lunes; //asigna el valor lunes a la variable ayer del tipo enumerado ayer=0; //equivalente a la asignación ayer=lunes En este ejemplo el valor de lunes es 0, de martes 1, y así sucesivamente. Variables Una variable se usa para poner nombre a posiciones de memoria RAM, debiendo ser declaradas de forma obligatoria antes de poder usarlas. Para declarar una variable debemos indicar su nombre mediante un identificador válido y el tipo de dato que manejará. La sintaxis de declaración de variables es: tipo_dato mivariable [=Valor inicial]; donde tipo_dato es uno de los tipos de dato básico o complejo, del compilador o definido por el usuario y mivariable es un identificador válido cualquiera (que no sea palabra reservada) y, opcionalmente, se puede inicializar a un valor. Veamos algunos ejemplos de declaración de variables. unsigned char car1; char car2='a'; char car2=97; char car2=0x61; int i; //Variable entera de 8 bits sin signo //Variable entera de 8 bits para almacenar ASCII //Línea idéntica a la anterior //Línea idéntica a la anterior //Variable entera con signo 5 signed int i; unsigned int x; //Línea idéntica a la anterior //Variable entera sin signo También podemos declarar varias variables del mismo tipo en una sola línea separaándolas por comas, como por ejemplo: float area, volumen; unsigned char a, b, c; //Variables area y volumen de tipo float //Variables a, b y c como unsigned char Las variables tienen un determinado ámbito o área donde serán accecibles. Los tipos de ámbito más usuales corresponden a las variables globales y a las variables locales. Las variables locales solamente se usan (son accesibles) en la función donde son declaradas; las variables globales son accesibles desde cualquier punto del programa. Los dos ámbitos de variables deben declararse antes de usarlas y las globales deben declararse antes de cualquier función y, por supuesto fuera de ellas. Las variables globales se inicializan a cero cuando se entra en la función main(). Las variables locales deben declararse al principio de la función donde se van a usar. A continuación vemos un boceto de programa donde se aclaran estos términos. Especificadores de tipo Al declarar una variable podemos añadirle (anteponiendo al tipo) un especificador como const, static, volatile, extern, auto, etc. Dichos especificadores tienen diversas funciones que vamos a describir de forma breve seguidamente. Aunque las explicaciones siguientes no se entiendan perfectamente ya tendremos posteriormente los conocimientos necesarios. Variables const: deben ser inicializadas en su declaración. Después de eso el compilador solo permitirá su lectura pero no su escritura. Variables static: En primer lugar indicaremos que las variables static locales y globales no tienen el mismo significado. La variables locales de una función se crean cuando es llamada y se destruyen cuando se sale de ella. Los conceptor de crear y destruir debemos entenderlos como que tienen reservado espacio en memoria o que se libera dicho espacio. El significado de lo anterior es que el valor de las variables locales no tienen por que ser el mismo entre llamadas de función. Para que una variable tenga reservado espacio en memoria y su valor no cambie entre llamadas de función tenemos que declararla bien como global o bien como local estática. Por defecto las variables estáticas se inicializan a 0. Variables volatile: Este tipo se aplica tipicamente a variables enteras y sus derivados y básicamente es una indicación al compilador para que no guarde el 6 valor de la variable en un registro del micro, sino que fuerza la actualización en memoria RAM. Esto se hace cuando existe la posibilidad de que el valor de la variable sea modificado por otro proceso que se ejecuta concurrentemente con el actual, por ejemplo cuando usamos programación con hebras o hilos o bien usamos interrupciones. Un ejemplo típico puede ser algo como lo que sigue: volatile int bandera=0; //Declaración global ... while(!bandera) { //Ejecutar proceso mientras bandera sea falso ... } ... //Lo siguiente forma parte de una rutina de interrupción //y está en una función o método diferente del proceso. if(se_produce_interrupcion){ bandera=1; } ... Vemos que declaramos bandera como global para que sea acceisble al proceso y a la interrupción. Si no la declaramos como volatile el compilador puede guardar la variable en un registro del micro para acelerar el proceso lo que hará que se pierdan las modificaciones que introduzcamos mediante la interrupción, si esta se produce mientras estamos en el bucle while. Declarandola como volatile forzamos al compilador a generar código para actualizar el valor de la variable en memoria antes de verificar si es verdadero o falso. Variables extern: Permiten el uso de variables en compilaciones múltiples. Variables auto: El especificador auto se asume por defecto, por lo que no hay que indicarlo. La variable existe mientras esté activa la función, no se inicializan a cero y su valor desaparece cuando se sale de la función. Congruencia y conversiones entre tipos. Operador cast() Es evidente que para poder realizar operaciones aritméticas entre variables estas tienen que ser del mismo tipo. Si, por ejemplo, tenemos una de tipo int y otra tipo float, la primera se convierte a float, o dicho de otra forma, antes de efectuarse la operación la de menor rango convierte el tipo para que su rango sea el de la de mayor rango. Se trata de una conversión o adaptación de tipos que se realiza de forma automática por parte del compilador sin necesidad de intervención alguna por parte del programador. Ante situaciones de este tipo, y aunque no tengamos que intervenir, si es necesario conocer las reglas con las que se producen estos cambios. Los tipos vistos hasta ahora se ordenan por rangos de mayor a menor de la siguiente forma: float > unsigned long > long > unsigned int > int > char Esta conversión también se produce cuando el resultado de una expresión es asignado a una variable convirtiéndose dicho resultado al tipo de la variable. En estas conversiones debemos tener mucho cuidado porque si, por ejemplo, la variable a la que se asigna el resultado es de menor rango que el de la expresión perderemos información. Es decir, en una operación como la siguiente: float a= 5.4,b=12.0; int y; y = a*b-3*b+7; Cabría esperar que el valor final de y fuese 395,8 y en cambio es de 395. 7 Otro tipo de conversión es la que se realiza con el operador cast o explicita, que es una conversión de tipo, forzada por el programador. El operador cast consiste en preceder la constante, variable o expresión que se desea convertir por el tipo al que se desea convertir, encerrado entre paréntesis. Veamos un ejemplo: Vf = (int)3.5 + ((int)I * (int)R); Tanto las variables I como R sean del tipo que sean, como la constante 3.5, de tipo float, se convierten a tipo int. El operador cast se utiliza normalmente a valores de retorno de funciones y expresiones para asegurar el tipo. Cuando usamos el operador cast con la variable R decimos que estamos haciendo 'casting' a la variable R. Operadores Un operador es un símbolo especial que indica al compilador que debe efectuar una operación matemática o lógica. En C existen muchos operadores de diversos tipos que vamos a describir a continuación. Operadores Aritméticos Operador Operación + Suma - Resta * Multiplicación / División % Módulo o resto de una división entera ++ Incremento. Suma 1 al valor de la expresión -- Decremento. Resta 1 al valor de la expresión Ejemplos sizeof() Determina el tamaño en bytes del operando Todos estos operadores se pueden aplicar a constantes, variables y expresiones. El resultado es el que se obtiene de aplicar la operación correspondiente entre los dos operandos. El operador % se aplica solamente a constantes, variables o expresiones de tipo int y su significado lo vemos diciendo que el resultado de la operación 10%3 es 1 ya que el resto de dividir 10 entre 3 es 1. Una expresión es un conjunto de variables y constantes, como por ejemplo el siguiente polinomio: x*x/3.0-5.0*x+3.0 Las expresiones pueden contener paréntesis para agrupar términos. Puede haber paréntesis contenidos dentro de otros paréntesis. El significado de los paréntesis coincide con el habitual en las expresiones matemáticas. Los operadores incrementales pueden ir inmediatamente delante o detrás de la variable. Si preceden a la variable, ésta es incrementada antes de que el valor de dicha variable sea utilizado en la expresión en la que aparece y se denomina preincremento. Si es la variable la que precede al operador, la variable es incrementada después de ser utilizada en la expresión y se denomina posincremento. El operando del operador sizeof() pueder ser el identificador o el tipo de variable que 8 debemos declarar previamente. Aunque por los paréntesis pueda parecer una función se trata de un operador. Veamos como usarlo: int a=0, t=0; t=sizeof(a); //Guarda en t el tamaño del entero a t=sizeof(int); //Idéntico al anterior Operadores de Asignación Operador Operación Ejemplos = Asignación simple += Suma y asigna -= Resta y asigna *= Multiplica y asigna /= Divide y asigna %= Módulo y asigna <<= Desplaza izquierda y asigna >>= Desplaza asigna &= AND entre bits y asigna |= OR entre bits y asigna ^= XOR entre bits y asigna derecha y Los operadores de asignación atribuyen a una variable (escriben en la zona de memoria asignada a la variable) el resultado de una expresión o el valor de otra variable. El operador de asignación más usado es la igualdad (que no debemos confundir con la igualdad matemática). Su sintaxis general es: NombreVariable = expresion; Se evalúa expresion y el resultado se deposita en NombreVariable, sustituyendo cualquier otro valor que hubiera en esa posición de memoria. Una posible utilización de este operador es: variable = variable + 1; Desde un punto de vista matemático esto no tiene sentido, pero sí lo tiene considerando que en realidad el operador de asignación representa una sustitución; es decir, se toma el valor de variable que tenemos en memoria, se le suma uno y el valor resultante vuelve a guardarse en memoria. El resultado ha sido incrementar el valor de variable en una unidad. Operadores Relacionales Operador Operación < Menor que > Mayor que <= Menor o igual que >= Mayor o igual que == Igual != Distinto ?: Condicional 9 Los operadores relacionales dan al lenguaje de programación la posibilidad de considerar alternativas, es decir proceder de una forma u otra según se cumplan o no ciertas condiciones analizando si se cumplen o no esas condiciones. Nosotros estamos acostumbrados a evaluar este tipo de condiciones en el lenguaje natural evaluando condiciones usando palabras como si (yes), no (no), conectado (on), desconectado (off), verdadero (true), falso (false) y otras muchas más. En informática se ha adoptado la forma true-false obteniendo un resultado de evaluación de una condición true si esta se cumple y false en caso contrario. En lenguaje C la condición false se representa por un 0 y cualquier otro valor representa true, aunque normalmente se adopta un 1 para true. El operador condicional tiene la sintaxis siguiente: PrimerOperando ? SegundoOperando : TercerOperando La expresión PrimerOperando debe ser una expresión booleana y la ejecución del operador condiconal se realiza así: Se evaluá PrimerOperando y si el resultado es verdadero el resultado de la expresión condicional es SegundoOperando. Si el resultado de evaluar PrimerOperando es falso, el resultado de la expresión condicional es TercerOperando. En resumen: Resultado de evaluar PrimerOperando Resultado del operador condiconal TRUE SegundoOperando FALSE TercerOperando Operadores Lógicos Operador Operación Comentarios && AND lógica Si al evaluar cada operando el resultado es verdadero da como resultado verdadero. Si uno de ellos es falso, el resultado es falso. Si el primer operando es falso, el segundo no se evalúa. || OR lógica Si al evaluar cada operando el resultado es falso da como resultado falso. Si uno de los operando es verdadero da como resultado verdadero. Si el primer operando es verdadero no se evalúa el segundo. ! NOT lógica El resultado es falso si al evaluar su operando el resultado es verdadero y verdadero en caso contrario. Son operadores binarios que permiten comprobar que se cumplen simultáneamente varias condiciones, que se cumple una u otra, etc. En lenguaje C los operadores lógicos responden a las funciones del álgebra de Boole AND (&&), OR (||) y NOT (!). El resultado de efectuar una operación lógica siempre será un valor lógico verdadero o falso (0 o 1). Es decir, toda expresión numérica con valor distintos de 0 origina un resultado verdadero y cuando la expresión numérica toma valor 0 origina un resultado falso. A continuación vemos algunos ejemplos. 10 Operadores Bitwise o de bits Operador Operación Comentarios & AND Compara dos bits generando un resultado 1 si ambos son uno, en caso contrario el resultado es 0. | OR Compara dos bits generando un resultado 1 si ambos o cualquiera de ellos es 1, en caso contrario el resultado es 0. ^ XOR Compara dos bits generando un resultado 1 si los bits son complementarios, en caso contrario el resultado es 0. ~ Complemento a 1 Realiza el complemento a 1 (cambia 1 por 0 y 0 por 1) del operando. El caracter del operador corresponde al ASCII 126 >> Desplazamiento a Descarta el bit menos significativo. Si es un número sin signo pone a 0 el bit la derecha más significativo, caso contrario desplaza el bit de signo por la izquierda. << Desplazamiento a Descarta el bit más significativo y rellena con 0 por la derecha. la izquierda 11 A continuación mostramos ejemplos de los operadores de bits. Punteros Operador Tipo Comentarios & Dirección Devuelve la dirección de memoria donde reside la variable. * Indirección Devuelve el contenido de una determinada dirección de memoria -> Puntero a estructura Da acceso a miembros de una estructura. Puesto que se trata de operadores los vamos a introducir en este momento e indicaremos que se trata de uno de los aspectos más potentes del lenguaje C, aunque bastante complicado de dominar. La sintaxis general de estos operadores es: *Expresion; &NombreVariable; El operador & se denomina de dirección y lo que hace es devolver la dirección de memoria donde reside la variable, en el ejemplo NombreVariable. Por ejemplo, una vez ejecutada la siguiente sentencia: NVar1= &Nvar2; NVar1 contiene la dirección de memoria donde se guarda el contenido de NVar2. Las variables que almacenan direcciones de otras variables se denominan punteros, y deben ser declaradas como tales, teniendo su propia sintaxis y forma de funcionamiento que veremos en otro tema. El operador * se denomina de indirección siendo el complementario del operador de dirección &, es decir devuelve el contenido de una determinada dirección de memoria. Por ejemplo, en la siguiente sentencia NVar3=*NVar1; el puntero Nvar1 (contenido de la dirección de memoria representada por la variable Nvar1) se asigna a la variable NVar3. 12 Jerarquía de operaciones. Precedencia de operadores Las operaciones se efectúan en un orden jerárquico que si no se tiene en cuenta al plantear y resolver una determinada operación casi siempre conduce a resultados equivocos o no deseados como estos: Consideremos la expresión 2 + 3 * 4 1. Si hacemos primero 2+3=5 y después el producto 5*4, el resultado es 20. 2. Si realizamos primero 3*4=12 y luego la suma 2+12, el resultado es 14. 3. Si notaA=6 y notaB=8 y calculamos media= notaA+notaB/2 podemos obtener dos posibles resultados 10 y 7 según el orden de evaluación del operador. Para que el el resultado de cada expresión sea claro e inequívoco, es necesario definir las reglas que definen el orden con el que se ejecutan las operaciones en lenguaje C. Antes de plantear una formula en un programa debemos evaluar el siguiente orden de operaciones: 1. Paréntesis 2. Potencias y raíces 3. Multiplicaciones y divisiones 4. Sumas y restas 5. Dos o más de la misma jerarquía se resuelven de izquierda a derecha En caso de duda usar siempre paréntesis. En la tabla siguiente vemos el orden descendente de precedencia de los operadores tal y como aparece en la ayuda de CCS. (expr) !expr ~expr ++expr expr++ (type)expr *expr &value sizeof(type) expr*expr expr/expr expr%expr expr+expr expr-expr expr<<expr expr>>expr expr<expr expr<=expr expr==expr expr!=expr expr>expr expr&expr expr^expr expr | expr expr&&expr expr || expr expr ? expr: expr lvalue = expr lvalue+=expr lvalue-=expr lvalue*=expr lvalue/=expr lvalue%=expr lvalue>>=expr lvalue<<=expr lvalue&=expr lvalue^=expr lvalue|=expr expr, expr 13 expr>=expr --expr Expr--