Types, Operators and Expressions



  • Variables and constants are the basic data objects manipulated in a program.


  • Declarations list the variables to be used, and state what type they have and perhaps what their initial values are.


  • Operators specify what is to be done to them.


  • Expressions combine variables and constants to produce new values. The type of an object determines the set of values it can have and what operations can be performed on it.




Variable Names


  • There are some restrictions on the names of variables and symbolic constants.


    1. Names are made up of letters and digits; the first character must be a letter.The underscore ``_" counts as a letter;


    2. Upper and lower case letters are distinct, so x and X are two different names. Traditional C practice is to use lower case for variable names, and all upper case for symbolic constants.


    3. Keywords like if, else, int, float, etc., are reserved: you can't use them as variable names. They must be in lower case.



Data Types and Sizes


  • There are only a few basic data types in C:


    1. char : a single byte, capable of holding one character in the local character set


    2. int : an integer, typically reflecting the natural size of integers on the host machine


    3. float : single-precision floating point.


    4. double : double-precision floating point


  • There are a number of qualifiers that can be applied to these basic types : short and long apply to integers. E.g : short int sh; long int counter; The word int can be omitted in such declarations.


  • Each compiler is free to choose appropriate sizes of int, short, long for its own hardware, subject only to the the restriction that shorts and ints are at least 16 bits, longs are at least 32 bits, and short is no longer than int, which is no longer than long.




Signed or Unsigned ?


  • The qualifier signed or unsigned may be applied to char or any integer.


  • unsigned numbers are always positive or zero, and obey the laws of arithmetic modulo 2n, where "n" is the number of bits in the type.


  • If chars are 8 bits, unsigned char variables have values between 0 and 255, while signed chars have values between -128 and 127 (in a two's complement machine.)


  • The type long double specifies extended-precision floating point. As with integers, the sizes of floating-point objects are implementation-defined;




The complete set of escape sequences



  • \a alert (bell) character


  • \b backspace


  • \f formfeed


  • \n newline


  • \r carriage return


  • \t horizontal tab


  • \v vertical tab


  • \\ backslash


  • \? question mark


  • \' single quote


  • \" double quote


  • \ooo octal number


  • \xhh hexadecimal number


  • '\0' represents the character with value zero, the null character



Constant Expression


  • A constant expression is an expression that involves only constants. Such expressions may be evaluated at during compilation rather than run-time.


  • #define MAXLINE 1000
       char line[MAXLINE+1];

  • A string constant, or string literal, is a sequence of zero or more characters surrounded by double quotes, as in "I am a string"


  • String constants can be concatenated at compile time: "hello, " "world" is equivalent to "hello, world".


  • Be careful to distinguish between a character constant and a string that contains a single character: 'x' is not the same as "x".


  • There is one other kind of constant, the enumeration constant. An enumeration is a list of constant integer values, as in enum boolean { NO, YES }; The first name in an enum has value 0, the next 1, and so on, unless explicit values are specified.


  • If not all values are specified, unspecified values continue the progression from the last specified value, as the second of these examples:


    1. enum escapes { BELL = '\a', BACKSPACE = '\b', TAB = '\t',
                        NEWLINE = '\n', VTAB = '\v', RETURN = '\r' };

    2. enum months { JAN = 1, FEB, MAR, APR, MAY, JUN,
                       JUL, AUG, SEP, OCT, NOV, DEC };
                             /* FEB = 2, MAR = 3, etc. */




Declarations



  • All variables must be declared before use. A declaration specifies a type, and contains a list of one or more variables of that type. Variables can be distributed among declarations in any fashion.


  • int  lower, upper, step;
       char c, line[1000];

  • A variable may also be initialized in its declaration. If the name is followed by an equals sign and an expression, the expression serves as an initializer


  • char  esc = '\\';
       int   i = 0;
       int   limit = MAXLINE+1;
       float eps = 1.0e-5;

  • The qualifier const can be applied to the declaration of any variable to specify that its value will not be changed. For an array, the const qualifier says that the elements will not be altered.


  • const double e = 2.71828182845905;
       const char msg[] = "warning: ";



Arithmetic Operators



  • The binary arithmetic operators are +, -, *, /, and the modulus operator %.


  • Integer division truncates any fractional part.


  • The % operator cannot be applied to a float or double.


  • The direction of truncation for "/ (division)" and the sign of the result for "% (modulus operator)" are machine-dependent for negative operands, as is the action taken on overflow or underflow.


  • The binary + and - operators have the same precedence, which is lower than the precedence of *, / and %, which is in turn lower than unary + and -.


  • Arithmetic operators associate left to right.





Relational and Logical Operators



  • The relational operators are : > (greater than), >= (greater than equal to), < (less than), <= (less than equal to). They all have the same precedence.


  • Relational operators have lower precedence than arithmetic operators, so an expression like i < lim-1 is taken as i < (lim-1).


  • The logical operators && (AND) and || (OR). Expressions connected by && or || are evaluated left to right, and evaluation stops as soon as the truth or falsehood of the result is known.


  • The precedence of && is higher than that of ||, and both are lower than relational and equality operators.


  • The numeric value of a relational or logical expression is 1 if the relation is true, and 0 if the relation is false.


  • The unary negation operator ! (NOT) converts a non-zero operand into 0, and a zero operand in 1. A common use of ! is in constructions like if (!valid) rather than if (valid == 0)




Type Conversions



  • When an operator has operands of different types, they are converted to a common type.


  • The only automatic conversions are those that convert a ``narrower" operand into a ``wider" one without losing information, such as converting an integer into floating point.


  • Expressions that might lose information, like assigning a longer integer type to a shorter, or a floating-point type to an integer, may draw a warning, but they are not illegal.


  • A char is just a small integer, so chars may be freely used in arithmetic expressions. Example : s - '0' gives the numeric value of the character stored in s. Also a second example s + 'a' - 'A' is to convert an upper case character stored in s to lower case but this applies to ASCII only.


  • This applies to ASCII only because corresponding upper case and lower case letters are a fixed distance apart as numeric values and each alphabet is contiguous -- there is nothing but letters between A and Z. This latter observation is not true of the EBCDIC character set.


  • When a char is converted to an int, can it ever produce a negative integer? The answer varies from machine to machine, reflecting differences in architecture. On some machines a char whose leftmost bit is 1 will be converted to a negative integer (``sign extension"). On others, a char is promoted to an int by adding zeros at the left end, and thus is always positive.


  • ASCII


Additional Notes :


  • Relational expressions like i > j and logical expressions connected by && and || are defined to have value 1 if true, and 0 if false.


  • In the test part of if, while, for, etc., ``true'' just means ``non-zero", so this makes no difference.


  • precedence of operators in c
  • Rules for Type Conversions :


    1. If either operand is long double, convert the other to long double.


    2. If either operand is double, convert the other to double.


    3. If either operand is float, convert the other to float.


    4. Convert char and short to int. If either operand is long, convert the other to long. Floats in an expression are not automatically converted to double. float to int causes truncation of any fractional part. When a double is converted to float, whether the value is rounded or truncated is implementation dependent



Explicit type conversions - unary operator (cast)


  • Explicit type conversions can be forced (``coerced") in any expression, with a unary operator called a cast. Syntax : (type name) expression


  • Example : sqrt((double) n) can be used to convert the value of "n" to double. Note that the cast produces the value of "n" in the proper type; "n" itself is not altered. The cast operator has the same high precedence as other unary operators.


  • If arguments are declared by a function prototype the declaration causes automatic coercion of any arguments when the function is called. Example : double sample_function(double) changes coerces the integer argument passed to the function into the double value 2.0 without any need for a cast.