Operators and Punctuators

List of Operators and Punctuators

The standard C language provides the following operators and punctuators:

{ }

[ ]

( )

(type)

.

->

++

--

&

*

+

-

~

!

sizeof

/

%

<<

>>

<

>

<=

>=

==

!=

^

|

&&

||

? :

=

*=

/=

%=

+=

-=

<<=

>>=

&=

^=

|=

,

#

##

;

:

" "

...

In addition, GNU C also knows the typeof operator, and extends some existing operators.


Categories of Operators and Punctuators

The operators # and ## are used only by the preprocessor.

Depending on context, the same operator can have more than one meaning. For example, the ampersand ('&') can be interpreted as

In the first case, the '&' is a unary operator; in the second, the '&' is a binary operator. Similarly, the comma ',' may act as an operator and as a punctuator, etc.

Unary operators

Note: In GNU C the operator '&&' may also be used as unary operator for taking addresses of labels.

Referencing operator ('&')

In the expression

& expr

which means "take the address of the expr", the expr operand must be one of the following:

If the operand is of type type, the result is of type "pointer to type".

The '&' symbol is also used in C as a binary bitwise AND operator.

Dereferencing operator ('*')

In the expression

* expr

which means "the object pointed to by expr", the expr must have type "pointer to type," where type is any data type. The result of the indirection is of type type.

If the operand is of type "pointer to function", the result is a function designator. If the operand is a pointer to an object, the result is an lvalue designating that object.

In the following situations, the result of indirection is undefined:

  1. The expr is a null pointer.

  2. The expr is the address of an automatic (local) variable and execution of its block has terminated.

You can also use the asterisk as an operator to dereference a pointer, or as the multiplication operator. Asterisk may be used also as a punctuator for creating pointer types.

Unary plus and minus operators ('+' and '-')

In these unary + - expressions

+ expr
- expr

the expr operand must be of arithmetic type. The result is the value of the operand after any required integral promotions for the unary plus ('+') operator, or negative of the value of the operand after any required integral promotions for the unary minus ('-') operator. Floating point negation is internally executed using the fneg function.

Note that both '+' and '-' operators also have a binary form.

Increment operator ('++')

The increment operator may be used as a postincrement or preincrement operator:

expr ++           (postincrement)
++ expr           (preincrement)

The expression is called the operand; it must be of scalar type (arithmetic or pointer types) and must be a modifiable lvalue.

When postincrement operator form is used, the value of the whole expression is the value of the postfix expression before the increment is applied. After the postfix expression is evaluated, the operand is incremented by 1.

When preincrement operator form is used, the operand is incremented by 1 before the expression is evaluated; the value of the whole expression is the incremented value of the operand.

The increment value is appropriate to the type of the operand. Pointer types follow the rules for pointer arithmetic. In other words, the actual address on which a pointer points to is incremented by the size of the pointed object (not by 1, except if the pointed object is one byte long), so after the incrementing, the pointer points to the next object in a sequence (e.g. to a next element in an array, etc.).

Note: GNU C extends the pointer arithmetic to be valid even on void pointers and pointers to functions (see extended pointer arithmetic for more info).

Decrement operator ('--')

The decrement operator may be used as a postdecrement or predecrement operator:

expr --           (postdecrement)
-- expr           (predecrement)

The decrement operator follows the same rules as the increment operator, except that the operand is decremented by 1 after or before the whole expression is evaluated.

Logical negation operator ('!')

In the expression

! expr

the expr operand must be of scalar type (i.e. not an array, a structure or an union). The result is of type int and is the logical negation of the operand:

The expression '!expr' is equivalent to '(0 == expr)'.

Bitwise complement operator ('~')

In the expression

~ expr

the expr operand must be of integral type. The result is the bitwise complement of the operand after any required integral promotions. Each 0 bit in the operand is set to 1, and each 1 bit in the operand is set to 0.

Binary operators

Binary plus and minus operators ('+' and '-')

Both '+' and '-' uses the same syntax:

expr1 + expr2
expr1 - expr2

Note that both '+' and '-' operators also have an unary form.

Legal operand types for expr1 + expr2 are:

  1. Both expr1 and expr2 are of arithmetic type;

  2. expr1 is of pointer to object type, and expr2 is of integral type.

  3. expr1 is of integral type, and expr2 is of pointer to object type;

In case 1, the operands are subjected to the standard arithmetical conversions (for example, chars are promoted to ints), and the result is the arithmetical sum of the operands.

In cases 2 and 3, the rules of pointer arithmetic apply. When expr1 is of pointer type (case 2), the actual address on which a pointer points to is incremented by expr2 multiplied by the size of the pointed object (not just by expr2, except if the pointed object is one byte long). For example, if expr1 points to an array, expr1 + 5 points to a fifth element of the array, no matter how long are the particular elements of the array. The same rules are valid for the case 3. Assuming that ptr is a pointer to type and that N is an integer, and assuming that the CPU uses linear addressing (this is true on Motorola 68000, but not on Intel 8086 for example), expression

ptr + N

is equal to

(type *) ((long) ptr + N * sizeof (type))

Legal operand types for expr1 - expr2 are:

  1. Both expr1 and expr2 are of arithmetic type;

  2. expr1 is of pointer to object type, and expr2 is integral type;

  3. Both expr1 and expr2 are pointers to compatible object types;

In case 1, the operands are subjected to the standard arithmetic conversions, and the result is the arithmetic difference of the operands.

In cases 2 and 3, the rules of pointer arithmetic apply. When expr1 is pointer and expr2 is integral type (case 2), the actual address on which a pointer points to is decremented by expr2 multiplied by the size of the pointed object (not just by expr2, except if the pointed object is one byte long). For example, if expr1 points to the fifth element of an array, expr1 - 2 points to a third element of the array, no matter how long are the particular elements of the array. When both expr1 and expr2 are pointers, the result of the substraction is the difference of actual addresses divided by the common size of pointed objects. For example, if expr1 and expr2 point to two elements of the same array, then expr2 - expr1 will be equal to the difference of actual indices of pointed elements.

The unqualified type 'type' is considered to be compatible with the qualified types 'const type', 'volatile type', and 'const volatile type'.

Floating point addition and substraction are internally executed using the fadd and fsub functions.

Note: GNU C extends the pointer arithmetic to be valid even on void pointers and pointers to functions (see extended pointer arithmetic for more info).

Multiplicative operators ('*', '/' and '%')

There are three multiplicative operators in C:

* (multiplication: the product of the two operands)
/ (division: the quotient of the first operand divided by the second operand)
% (modulus: the remainder of the first operand divided by the second operand)

They use the following syntax:

expr1 * expr2
expr1 / expr2
expr1 % expr2

Note that '*' operator also has an unary form, and may be also used as a punctuator for creating pointer types.

Operands for '*' and '/' are of arithmetical type, and operands of '%' are of integral type. The usual arithmetic conversions are made on the operands. For '/' and '%', expr2 must be nonzero; expr2 == 0 results in an error (you can't divide by zero).

When expr1 and expr2 are integers and the quotient is not an integer:

  1. If expr1 and expr2 have the same sign, expr1 / expr2 is the largest integer less than the true quotient, and expr1 % expr2 has the sign of expr1.

  2. If expr1 and expr2 have opposite signs, expr1 / expr2 is the smallest integer greater than the true quotient, and expr1 % expr1 has the sign of expr1.

Note: Rounding is always toward zero.

Floating point multiplication and division are internally executed using the fmul and fdiv functions; more detailed info about rules of these operation is given with the description of these functions.

Bitwise shift operators ('<<' and '>>')

Bitwise shift operators in C use the following syntax:

expr1 << expr2
expr1 >> expr2

In the expressions expr1 << expr2 and expr1 >> expr2, the operands expr1 and expr2 must be of integral type. The normal integral promotions are performed on expr1 and expr2, and the type of the result is the type of the promoted expr1. If expr2 is negative or is greater than or equal to the width in bits of expr1, the operation is undefined.

The result of the operation expr1 << expr2 is the value of expr1 left-shifted by expr2 bit positions, zero-filled from the right if necessary. The result of the operation expr1 >> expr2 is the value of expr1 right-shifted by expr2 bit positions.

Logical and bitwise operators ('&', '^', '|', '&&' and '||')

The C language offers these bitwise and logical operators:

&  (bitwise AND)
^  (bitwise exclusive OR)
|  (bitwise inclusive OR)

&& (logical AND)
|| (logical OR)

They use the following syntax:

expr1 & expr2
expr1 ^ expr2
expr1 | expr2
expr1 && expr2
expr1 || expr2

In first three expressions, both operands must be of integral type. In fourth and fifth expressions, both operands must be of scalar type. The usual arithmetical conversions are performed on expr1 and expr2.

For the bitwise operators, each bit in the result is:

Bit valueResults of
  in expr1    in expr2  expr1 & expr2expr1 ^ expr2expr1 | expr2
00000
10011
01011
11101

Unlike the bitwise operators, '&&' and '||' guarantee left-to-right evaluation. expr1 is evaluated first; if it is zero, expr1 && expr2 gives 0 (false), and expr2 is not evaluated at all. With expr1 || expr2, if expr1 is nonzero, expr1 || expr2 gives 1 (true), and expr2 is not evaluated at all.

Note: In GNU C the operator '&&' may be also used as unary operator for taking addresses of labels.

Assignment operators ('=' etc.)

There are 11 assignment operators in C language. The '=' operator is the simple assignment operator; the other 10 ('*=', '/=', '%=', '+=', '-=', '<<=', '>>=', '&=', '^=' and '|=') are known as compound assignment operators. All of them use the following syntax:

expr1 assignment-operator expr2

In the expression expr1 = expr2, expr1 must be a modifiable lvalue. The value of expr2, after conversion to the type of expr1, is stored in the object designated by expr1 (replacing expr1's previous value). The value of the assignment expression is the value of expr1 after the assignment. That's why multiple assignments like

x = y = z = 10;
a = b + 2 * (c = d - 1);

are possible. Note that the assignment expression is not itself an lvalue.

For both simple and compound assignment, the operands expr1 and expr2 must obey one of the following sets of rules:

  1. expr1 is of qualified or unqualified arithmetic type and expr2 is of arithmetic type.

  2. expr1 has a qualified or unqualified version of a structure or union type compatible with the type of expr2.

  3. expr1 and expr2 are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right.

  4. One of expr1 or expr1 is a pointer to an object or incomplete type and the other is a pointer to a qualified or unqualified version of void. The type pointed to by the left has all the qualifiers of the type pointed to by the right.

  5. expr1 is a pointer and expr2 is a null pointer constant.

The compound assignments are 'op=', where op can be any one of the ten operator symbols '*', '/', '%', '+', '-', '<<', '>>', '&', '^' or '|'. The expression

expr1 op= expr2

has the same effect as

expr1 = expr1 op expr2

except that the lvalue expr1 is evaluated only once. For example, expr1 += expr2 is the same as expr1 = expr1 + expr2.

Relational operators ('<', '>', '<=' and '>=')

The relational operators are used to compare relative values. They use the following syntax:

expr1 < expr2
expr1 > expr2
expr1 <= expr2
expr1 >= expr2

In all relational expressions, the operands must conform to one of the following sets of conditions:

  1. Both expr1 and expr2 are of arithmetic type. In this case, the usual arithmetic conversions are performed and the result is of type int.

  2. Both expr1 and expr1 are pointers to qualified or unqualified versions of compatible object types.

When the operands are of arithmetic type:

When the operands are of compatible pointer types, the result depends on the relative addresses of the two objects being pointed at.

Floating point comparisons are internally executed using the fcmp function. See the description of this function for more info about rules of comparisons for floating point values.

Equality operators ('==' and '!=')

The operators '==' and '!=' are used to test for equality or inequality between arithmetic or pointer values, following rules similar to those for the relational operators. However, the equality operators have lower precedence than the relational operators, and you can also compare certain pointer types not allowed with relational operations.

In the expressions expr1 == expr2 and expr1 != expr2, the operands must conform to one of the following sets of conditions:

  1. Both expr1 and expr2 are of arithmetic type.

  2. Both expr1 and expr2 are pointers to qualified or unqualified versions of compatible types.

  3. One of expr1 and expr2 is a pointer to an object, and the other is a pointer to a qualified or unqualified version of void. In this case, the pointer to an object is converted to the type of the other operand (a void pointer).

  4. One of expr1 or expr2 is a pointer and the other is a null pointer constant.

If expr1 and expr2 have types that are valid operand types for a relational operator, the same comparison rules as for the relational operators apply.

If expr1 and expr2 are pointers to function types, expr1 == expr2 gives 1 (true) if they are both null or if they both point to the same function. Conversely, if expr1 == expr2 gives 1 (true), then either expr1 and expr2 point to the same function, or they are both null.

The expression expr1 != expr2 follows the same rules, except that the result is 1 (true) if the operands are unequal, and 0 (false) if the operands are equal.

Floating point comparisons are internally executed using the fcmp function. See the description of this function for more info about rules of comparisons for floating point values.

Selection (structure-access) operators ('.' and '->')

The C language supports two selection operators:

.  (direct member selector)
-> (indirect, or pointer, member selector)

You use the selection operators '.' and '->' to access structure and union members. Suppose that the object s is of struct type S and sptr is a pointer to s. Then, if m is a member identifier of type M declared in S, these expressions:

s.m
sptr->m

are of type M, and both represent the member object m in s.

The expression

sptr->m

is a convenient synonym for (*sptr).m.

The direct member selector ('.') uses the following syntax:

expresssion . identifier

The expr must be of type union or structure. The identifier must be the name of a member of that structure or union type.

The indirect member operator ('->') uses the following syntax:

expr -> identifier

The expr must be of type pointer to structure or pointer to union. The identifier must be the name of a member of that structure or union type.

The expression with selection operators designates a member of a structure or union object. The value of the selection expression is the value of the selected member; it will be an lvalue if and only if the expr is an lvalue. For example,

struct mystruct
  {
    int i;
    char str[21];
    long d;
  } s, *sptr=&s;

...

s.i = 3;              // assign to the 'i' member of mystruct 's'
sptr->d = 12345678;   // assign to the 'd' member of mystruct 's'

The expression 's.m' is an lvalue, provided that 's' is an lvalue and 'm' is not an array type. The expression 'sptr->m' is an lvalue unless 'm' is an array type.

If structure B contains a field whose type is structure A, the members of A can be accessed by two applications of the member selectors.

Ternary operators

Conditional operator ('? :')

The conditional operator '?:' is, in fact, a ternary operator. It uses the following syntax:

expr1 ? expr2 : expr3

In the expression expr1 ? expr2 : Expr3, the operand expr1 must be of scalar type. The operands expr2 and Expr3 must obey one of the following sets of rules:

  1. Both of arithmetic type. In this case, both expr2 and Expr3 are subject to the usual arithmetic conversions, and the type of the result is the common type resulting from these conversions.

  2. Both of compatible structure or union types. In this case, the type of the result is the structure or union type of expr2 and expr3.

  3. Both of void type. In this case, the result is of type void.

  4. Both of type pointer to qualified or unqualified versions of compatible types. In this case, the type of the result is pointer to a type qualified with all the type qualifiers of the types pointed to by both operands.

  5. One operand of pointer type, the other a null pointer constant In this case, the type of the result is pointer to a type qualified with all the type qualifiers of the types pointed to by both operands.

  6. One operand of type pointer to an object, the other of type pointer to a qualified or unqualified version of void. In this case, the type of the result is that of the non-pointer-to-void operand.

In all cases, expr1 is evaluated first. If its value is nonzero (true), then expr2 is evaluated and expr3 is ignored (not evaluated at all). If expr1 evaluates to zero (false), then expr3 is evaluated and expr2 is ignored. The result of expr1 ? expr2 : expr3 will be the value of whichever of expr2 and expr3 is evaluated.

Note: GNU C extends the usage of the conditional operator to allow omitting the middle operand, so it may be used as a binary operator too.

Punctuators

Most of these punctuators also function as operators.

Array subscript operator ('[...]')

Brackets indicate single and multidimensional array subscripts. When used as an operator, the expression

expr1[expr2]

is defined exactly as

*((expr1) + (expr2))

where either expr1 is a pointer and expr2 is an integer, or expr1 is an integer and expr2 is a pointer. Of course, the addition is performed in according to the pointer arithmetic rules (see binary plus for more info).

Note that every array name, if used alone (without the array subscript operator), is automatically interpreted as a pointer to the first element of the array.

When used as a punctuator, brackets are used for creating array types (see asterisk for more info).

Note: The GNU C extends the usage of square brackets to allow labeling elements in initializers.

Parentheses operators ('(...)')

Parentheses operators do the following:

Note that parentheses are also the part of the TypeCast operator.

When used as function-call operators, parentheses use the following syntax:

expr (arg-expression-list)

This is a call to the function given by the expr, which can be either the function name, or an expression which evaluates to a pointer-to-function type. In the second case, the function call is in fact translated to

(* expr) (arg-expression-list)

arg-expression-list is a comma-delimited list of expressions of any type representing the actual (or real) function arguments. The value of the function call expression, if it has a value, is determined by the return statement in the function definition.

arg-expression-list may even be empty, which is necessary when you need to call an argument-less function:

expr ()

Note that every function name, if used alone (without the parentheses operator), is automatically interpreted as a pointer to the function.

When used as a punctuator, parentheses are used for creating function types (see asterisk for more info).

Braces ('{...}')

The { } braces indicate the start and end of a compound statement. Each sequence of statements (terminated by semicolons) is treated as a single statement, called compound statement. Compound statements may have its own local variables as well.

Braces are also used in declaration of enumerations, structures and unions, as well as for function definitions. For example:

int square (int x);               // This is a function prototype
int square (int x) {return x*x;}  // This is a function definition

Note: The GNU C extends the usage of braces (together with parentheses) to allow making statement expressions. They are also used in cast constructors, which are yet another GNU C extension.

Equal sign ('=')

The '=' (equal sign) used as a punctuator separates variable declarations from initialization lists. For example,

int array[5] = { 1, 2, 3, 4, 5 };
char *name = "Fred";
int x = 12;

In a C function, no code can precede any variable declarations. Note that the GNU C, in opposite to other C dialects, allow non-constant initializers (like in C++).

The equal sign is also used in enumerations:

enum colors {Blue = 1, Red = 2, Green = 4, Light = 8};

It is also used as the assignment operator in expressions:

a = b + c;

or even:

a = b + 2 * (c = d - 1);

Note: The GNU C extends the usage of equal sign (as a punctuator) to allow labeling elements in initializers.

Comma operator and punctuator (',')

The comma, used as a punctuator, separates the elements of a function argument list, the elements in array or struct initializers, or the variables in a data declaration. The comma is also used as an operator in comma expressions. Mixing the two uses of comma is legal, but you must use parentheses to distinguish them.

When used as an operator, the comma operator uses the following syntax:

expr1, expr2

The left operand expr1 is evaluated as a void expression (i.e. it is ignored if not contain side effects), then expr2 is evaluated to give the result and type of the comma expression. So, the result is just expr2. By recursion, the expression

expr1, expr2, ..., ExprN

results in the left-to-right evaluation of each Expr-i, with the value and type of ExprN giving the result of the whole expression. Comma operator is usually used in for-loops for multiple initializations. For example,

for (i = 0, j = 1024; i + j < 5; i++, j /= 2) ...

It also may be used to avoid making compound statements in simple conditional statements. For example,

if (x > 10) i = 1, j = 2, k = 3;

have the same effect as

if (x > 10)
  {
    i = 1; j = 2; k = 3;
  }

To avoid ambiguity with the commas used in function argument and initializer lists, parentheses must be used. For example,

func(i, (j = 1, j + 4), k);

calls 'func' with three arguments, not four. The arguments are 'i', '5', and 'k'.

Semicolon (';')

The semicolon is a statement terminator. It is also used to separate three expressions which are parts of a for-loop.

Any legal C expression (including the empty expression) followed by ; is interpreted as a statement, known as an expression statement. The expression is evaluated and its value is discarded. If the expression statement has no side effects, it will be simply ignored.

Semicolons are often used to create an empty statement. For example, the body of the following for-loop (which searches for a first non-zero element in the array) is an empty statement:

for (i = 0; i < max || !a[i]; i++);

Colon (':')

Use the colon (':') to indicate a labeled statement:

start:
  x=0;
  ...
  goto start;

Special case of labels are case-labels, which are used in multiple selection statements, for example,

switch (a)
  {
    case 1:
      puts("One");
      break;
    case 2:
      puts("Two");
      break;
    ...
    default:
      puts("None of the above!");
  }

Note: The GNU C allows creating of local labels, which are useful in macro definitions.

Asterisk ('*')

The asterisk ('*') in a variable expression creates a pointer to a type. For example,

int a, *b;

In this example 'a' is an integer, but 'b' is a pointer to an integer.

Punctuators asterisk, brackets and parentheses may be mixed together to create very complex data types (which are sometimes very hard to understand). When mixed together, parentheses have the greatest precedence, then brackets, and finally, asterisks. This will be illustrated with a set of examples, which are given in the following table:

int x;

A simple integer

int x[5];

An array (with 5 elements) of integers

int x[5][6];

An array (with 5 elements) of arrays (with 6 elements) of integers; such "array of arrays" may be interpreted as a 5x6 matrix

int *x;

A pointer to an integer

int *x[5];

An array of pointers to integers

int x();

A function (more precise, a prototype of a function) which returns an integer

int x(int a);

A function which accepts one integer argument, and which returns an integer

int x(int a,int *b);

A function which accepts two arguments, the first one is an integer, and second one is a pointer to an integer, and which returns an integer

int x(int,int*);

The same as above, but actual names of arguments may be omitted in function prototypes (but not in function definitions)

int *x();

A function which returns a pointer to an integer

int *x(int);

A function which accepts one integer argument, and which returns a pointer to an integer

int **x;

A pointer to a pointer to an integer (this declaration is logically equivalent with the next one)

int *x[];

An array of unknown size of pointers to integers (this declaration is logically equivalent with the previous one)

int (*x)[5];

A pointer to an array (with 5 elements) of integers

int (*x)();

A pointer to a function which returns an integer

int (*x)(int,int);

A pointer to a function which accepts two integer arguments and which returns an integer

int (*x[5])();

An array of pointers to a function which returns an integer

int (*x(*x())[5])();

A function which returns a pointer to an array of pointers to a function which returns an integer

int (*x(*x(int))[5])(int*);

A function which accepts one argument which is a pointer to an integer, and which returns a pointer to an array of pointers to a function which accepts one integer argument and which returns an integer

int (*(*x[5])())[6];

An array (with 6 elements) of pointers to a function which returns a pointer to an array (with 5 elements) of integers

Confused? Yes, it's C!

If you simply omit the actual variable name, you will get an anonymous type. Such anonymous types may be used with sizeof or typecast operators, or in lists of arguments in function prototypes. For example, 'int*' is anonymous pointer to an integer, 'int(*)()' is anonymous pointer to a function which returns an integer, and 'int(*[])(int*)' is anonymous array (of unknown size) of pointers to a function which returns an integer and which accepts one argument which is a pointer to an integer.

Note: The GNU C extends the usage of asterisk to allow computed goto.

Quotes ('"..."')

Quotes are used for defining string constants. In the compile-time, the sequence of characters between qoutes are stored somewhere in the executable file. In the run time, the "result" of quotes is a pointer (of type 'char*') which points to the place where the sequence of characters is stored. This interpretation of quotes differs significantly from the interpretation of strings in other languages. That's why there is nothing wrong with the following C code (it stores the address where the text "Hello" is stored, interpreted as an integer, in a):

int a;
a = (int)"Hello";

Such constructs are usually impossible in other languages. Note that sequences of characters between quotes behave similarly like arrays declared with the static keyword, i.e. they can survive the end of a function or program if they are changed somewhere in the program (anyway, it is very bad idea to change the content of a string constant). For example, the following code

for (i = 0; i < 2; i++)
  {
    char *str;
    str = "xyz";
    printf (str);
    *str = 'a';
  }

will display "xyzayz", although it seems that "xyzxyz" would be displayed. Such strange behaviour is caused by the fact that "xyz" is initialized in the compile-time. Now, if you understand the correct interpretation of what the quote punctuator does, you can explain this behaviour easily. To learn: strings in C behave quite differently than in most other languages!

Note: These two statements are very different, although most books say that they are nearly the same:

char str[] = "Hello";
char *str = "Hello";

Suppose that str is declared in the body of the function (i.e. it is an automatic local variable). In both cases, the text "Hello" is stored somewhere in memory, and the "result" of "Hello" is the address where it is stored (we will call this address addr). In the first case, str is a local array (i.e. it is created on the stack at run time, and there will be 6 bytes reserved for it), whose content is initialized at run-time (because it does not exist at compile-time) with the sequence of bytes located at addr. In other words, it is the same as you wrote:

char str[6];
strcpy (str, "Hello");

The consequence is that if you change the content of str (note that this is not the same as changing bytes pointed to by addr), it will be reinitialized each time when this statement is encountered again. But, in the second case, str is a local pointer, whose content is initialized (at run-time) with addr! So, if the contents of the string "Hello" are changed, this change will be permanent, because when this statement is encountered again, str will simply be reinitialized (if changed) to addr but bytes pointed to by it are not restored! Confused? See the following code:

for (i = 0; i < 2; i++)
  {
    char str[] = "Hello";
    printf (str);
    str[0] = 'a';
  }

This program will work as expected (it will display "Hello" twice). But, if you change 'str[]' to '*str', it will not work as expected (it will display "Hello" and then "aello").

Generally, it is a very bad idea to change strings which are the result of quotes. The reason is that usually equal strings are stored only once in the program. This means that you can get unexpected results if you modify them.

If the '\' character is found inside a string literal, it is threated as the start of an escape code. Here is an incomplete list of possible escape codes:

\\ Represents one '\' character.
\" Represents one '"' character. Needed in strings to represent this character, because an unescaped '"' would end the string.
\n Newline; for ASCII this is octal code 012.
\b Backspace; for ASCII this is octal code 010.
\f FormFeed; for ASCII this is octal code 014.
\r Carriage-Return; for ASCII this is octal code 015.
\t Horizontal Tab; for ASCII this is octal code 011.
\ddd An octal character code. The numeric code is 3 octal digits.
\xdd... A hex character code. All trailing hex digits are combined.

Ellipsis ('...')

An ellipsis ('...') consists of three successive periods with no whitespace intervening. You can use an ellipsis in the formal argument lists of function prototypes to indicate a variable number of arguments, or arguments with varying types. For example,

void func (int n, char ch, ...);

This declaration indicates that func will be defined in such a way that calls must have at least two arguments, an int and a char, but can also have any number of additional arguments.

Note: The GNU C extends the usage of ellipsis for making case ranges and for labeling elements in initializers.

Typecast ('(type)')

The typecast operator forces an expression to behave as an object of a given type. It uses the following syntax:

(type) expr

The expression expr will be interpreted as having type type. It may be any simple type (modified or unmodified), enumeration, structure, union, user defined type created using typedef, void type, or an anonymous type (see asterisk for more information about anonymous types). Here is an example list of valid typecast operators:

(int)
(unsigned long int)
(signed)
(enum foo)
(struct foo)
(mytype)             assuming that 'mytype' is defined with 'typedef'
(void)
(char *)
(void *)
(struct foo *)
(void (*)(int))
(int (*[])(char*))

Typecast operator is very powerful in C, and may be used for conversion nearly everything to anything. For example, it is legal to convert an integer value to a pointer, so you can perform direct access to the memory (don't do this if you don't know exactly what are you doing). For example, '(char*)19456' converts integer 19456 to a char pointer, which may be dereferenced further using the dereference operator. So,

* (char*)19456 = 255;

stores 255 in the memory at the absolute address 19456 (this is the first byte of the LCD memory on TI-89 and TI-92+). The more drastic example is

((void(*)())10000)();

which calls the subroutine located at absolute address 10000 (first, the typecast operator '(void(*)())' converts 10000 to the pointer to the function, then the parentheses operator '()' is used to call the function). Note that nearly the whole TIGCC library, when used in "nostub" mode, is implemented using the typecast operator which converts TIOS jump table entries to pointers to functions. For example, ClrScr is defined as '(*(void(**)(void))(*(long*)0xC8+0x678))', which is nothing other than a complex typecast. So, when you do

ClrScr ();

in your program, the preprocessor replaces it with

(*(void(**)(void))(*(long*)0xC8+0x678))();

However, typecast operator cannot be used to convert a scalar type to a structure or union (and vice versa), or to convert one non-scalar type into an incompatible non-scalar type. That's why casting to structures and unions are rarely valid.

Casting to a void means that you want to discard the result of the expression.

Casting to or from a floating point type are internally executed using the flt and trunc functions. See the description of these functions for more info.

Note: GNU C extends the usage of typecast operator to allow making cast constructors, which are probably the most powerful GNU C extensions. It also allows much more flexibility in casting to an union types.


Precedence of Operators

In the following table of operator precedence, the C operators are divided into 15 categories. The #1 category has the highest precedence; category #2 (Unary operators) takes second precedence, and so on to the Comma operator, which has lowest precedence. The operators within each category have equal precedence.

The Unary (category #2), Conditional (category #13), and Assignment (category #14) operators associate right-to-left; all other operators associate left-to-right.

CategoryOperatorWhat it is (or does)
1. Highest [ ]
( )
->
.
Array subscript
Function call
Indirect component selector
Direct component selector
2. Unary !
~
+
-
++
--
&
*
sizeof
(type)
Logical negation (NOT)
Bitwise (1's) complement
Unary plus
Unary minus
Preincrement or postincrement
Predecrement or postdecrement
Address
Indirection
Size of operand, in bytes
TypeCast
3. Multiplicative *
/
%
Multiply
Divide
Remainder (modulus)
4. Aditive +
-
Binary plus
Binary minus
5. Shift <<
>>
Shift left
Shift right
6. Relational <
<=
>
>=
Less than
Less than or equal to
Greater than
Greater than or equal to
7. Equality ==
!=
Equal to
Not equal to
8. & Bitwise AND
9. ^ Bitwise XOR
10. | Bitwise OR
11. && Logical AND
12. || Logical OR
13. Conditional ? : "a ? x : y" means "if a then x, else y"
14. Assignment =
*=
/=
%=
+=
-=
&=
^=
|=
<<=
>>=
Simple assignment
Assign product
Assign quotient
Assign remainder (modulus)
Assign sum
Assign difference
Assign bitwise AND
Assign bitwise XOR
Assign bitwise OR
Assign left shift
Assign right shift
15. Comma , Evaluate


Lvalues and Rvalues

All expressions in C language may be classified to lvalues and rvalues.

An lvalue is an object locator: an expression that designates an object. Any variable is an lvalue for example. Another example of an lvalue expression is '*P', where P is any expression evaluating to a non-null pointer. See dereference operator for more information.

A modifiable lvalue is an identifier or expression that relates to an object that can be accessed and legally changed in memory. A const pointer to a constant, for example, is not a modifiable lvalue. A pointer to a constant can be changed (but its dereferenced value cannot).

Historically, the "l" stood for "left", meaning that an lvalue could legally stand on the left (the receiving end) of an assignment statement. Now, only modifiable lvalues can legally stand on the left of an assignment statement. For example, if 'a' and 'b' are nonconstant integer identifiers with properly allocated memory storage, they are both modifiable lvalues, and assignments such as

a = 1;
b = a + b;

are legal. From the other side, the expression

a + b

is not an lvalue: 'a + b = a' is illegal because the expression on the left is not related to an object. Such expressions are often called rvalues (short for "right values").

Note: In GNU C, the class of lvalue expressions is wider than in other C dialects; see section Generalized Lvalues for more info.


Return to the main index