C's modulo operator (%) produces the remainder of integer division, which equals 0 if the right operand divides the left operand exactly. This operator can be useful for tasks such as determining whether or not a year is a U.S. presidential election year. For example:
if (year % 4 == 0)
printf("This is an Olympic Games year.\n");
else
printf("There will be no Olympic Games this year.\n");
As required by the ANSI/ISO C standard, HP C supports the following relationship
between the remainder and division operators:
a equals a%b + (a/b) * b for any integer values of a and bThe result of a division or modulo division is undefined if the right operand is 0.
The sign reversal or unary minus operator (-) multiplies its sole operand by -1. For example, if x is an integer with the value -8, then -x evaluates to 8.
The result of the identity or unary plus operator (+) is simply the value of the operand.
Refer to Operator Precedence for information
about how these and other operators evaluate with respect to each other.
postfix-expression [ expression ]
The [ ] operator is defined so that E1[E2] is identical to (*((E1)+(E2))) in every respect. This leads to the (counterintuitive) conclusion that the [ ] operator is commutative. The expression E1[E2] is identical to E2[E1].
C's subscripts run from 0 to n-1 where n is the array size.
Multidimensional arrays are represented as arrays of arrays. For this reason, the notation is to add subscript operators, not to put multiple expressions within a single set of brackets. For example, int x[3][5] is actually a declaration for an array of three objects. Each object is, in turn, an array of five int. Because of this, all of the following expressions are correct:
x x[i] x[i][j]The first expression refers to the 3 by 5 array of int. The second refers to an array of five int, and the last expression refers to a single int.
The expression x[y] is an lvalue.
There is no arbitrary limit on the number of dimensions that you can declare in an array.
Because of the design of multidimensional C arrays, the individual data objects must be stored in row-major order.
As another example, the expression
a[i,j] = 0looks as if array a were doubly subscripted, when actually the comma in the subscript indicates that the value of i should be discarded and that j is the subscript into the a array.
x = 3; /* assigns the value 3 to variable x */ x = y; /* assigns the value of y to x */ x = (y*z); /* performs the multiplication and assigns the result to x */An assignment expression itself has a value, which is the same value that is assigned to the left operand.
The assignment operator has right-to-left associativity, so the expression
a = b = c = d = 1;is interpreted as
(a = (b = (c = (d = 1))));First 1 is assigned to d, then d is assigned to c, then c is assigned to b, and finally, b is assigned to a. The value of the entire expression is 1. This is a convenient syntax for assigning the same value to more than one variable. However, each assignment may cause quiet conversions, so that
int j; double f; f = j = 3.5;assigns the truncated value 3 to both f and j. Conversely,
j = f = 3.5;assigns 3.5 to f and 3 to j.
i = i + 10;you can use the assignment operator format to shorten the statement to
i += 10;In other words, any statement of the form
var = var op exp; /* traditional form */can be represented in the following shorthand form:
var op = exp; /* shorthand form */where var is a variable, op is a binary operator, and exp is an expression.
The only internal difference between the two forms is that var is evaluated only once in the shorthand form. Most of the time this is not important; however, it is important when the left operand has side effects, as in the following example:
int *ip; *ip++ += 1; /* These two statements produce */ *ip++ = *ip++ + 1; /* different results. */The second statement is ambiguous because C does not specify which assignment operand is evaluated first. See Operator Precedence for more information concerning order of evaluation.
int i; i = 3.5;
c = 882;The binary representation of 882 is
00000011 01110010This number requires two bytes of storage, but the variable c has only one byte allocated for it, so the two upper bits don't get assigned to c. This is known as overflow, and the result is not defined by the ANSI/ISO C standard or the K&R language definition for signed types. HP C simply ignores the extra byte, so c would be assigned the rightmost byte:
01110010This would erroneously give c the value of 114. The principle illustrated for chars also applies to shorts, ints, and long ints. For unsigned types, however, C has well-defined rules for dealing with overflow conditions. When an integer value x is converted to a smaller unsigned integer type, the result is the non-negative remainder of
x / (U_MAX+1)where U_MAX is the largest number that can be represented in the shorter unsigned type. For example, if j is an unsigned short, which is two bytes, then the assignment
j = 71124;assigns to j the remainder of
71124 / (65535+1)The remainder is 5588. For non-negative numbers, and for negative numbers represented in two's complement notation, this is the same result that you would obtain by ignoring the extra bytes.
f = 10;is executed as if it had been written
f = 10.0;This conversion is invisible. There are cases, however, where a floating-point type is not capable of exactly representing all integer values. Even though the range of floating-point values is generally greater than the range of integer values, the precision may not be as good for large numbers. In these instances, conversion of an integer to a floating-point value may result in a loss of precision. Consider the following example:
include <stdio.h>
int main(void)
{
long int j = 2147483600;
float x;
x = j;
printf("j is %d\nx is %10f\n", j, x);
exit(0);
}
If you compile and execute this program, you get:
j is 2147483600 x is 2147483648.000000
j = 2.5;This causes a loss of precision which could have a dramatic impact on your program. The same truncation process occurs for negative values. After the assignment
j = -5.8;the value of j is -5.
An equally serious situation occurs when the floating-point value cannot fit into an integer. For example:
j = 999999999999.0This causes an overflow condition which will produce unpredictable results. As a general rule, it is a good idea to keep floating-point and integer values separate unless you have a good reason for mixing them.
f = 1.0123456789the computer rounds the double constant value before assigning it to f. The value actually assigned to f, therefore, will be 1.012346 (in double-to-float conversions, HP C always rounds to the nearest float value). The following example shows rounding due to conversions.
/* Program name is "float_rounding". It shows how double values
can be rounded when they are assigned to a float. */
#include <stdio.h>
int main(void)
{
float f32;
double f64;
int i;
for (i = 1, f64 = 0; i < 1000; ++i)
f64 += 1.0/i;
f32 = f64;
printf("Value of f64: %1.7f\n", f64);
printf("Value of f32: %1.7f\n", f32);
}
The output is
Value of f64: 7.4844709 Value of f32: 7.4844708
f = 2e40;In this simple case, the compiler recognizes the problem and reports a compile-time error. In other instances, however, a run-time error could result.
/* Following are examples of the use of each * assignment operator. In each case, x = 5 * and y = 2 before the statement is executed. */ x = y; x = 2 x += y + 1; x = 8 x -= y * 3; x = -1 x *= y + 1; x = 15 x /= y; x = 2 x %= y; x = 1 x <<= y; x = 20 x >>= y; x = 1 x &= y; x = 0 x ^= y; x = 7 x |= y; x = 7 x = y = 1 x = 1, y = 1
short int to_the_left = 53, to_the_right = 53; short int left_shifted_result, right_shifted_result; left_shifted_result = to_the_left << 2; right_shifted_result = to_the_right >> 2;sets left_shifted_result to 212 and right_shifted_result to 13. The results are clearer in binary:
base 2 base 10 0000000000110101 53 0000000011010100 212 /* 53 shifted left 2 bits */ 0000000000001101 13 /* 53 shifted right 2 bits */Shifting to the left is equivalent to multiplying by powers of two:
x << y is equivalent to x * 2y.Shifting non-negative integers to the right is equivalent to dividing by powers of 2:
x >> y is equivalent to x / 2y.The << operator always fills the vacated rightmost bits with zeros. If exp1 is unsigned, the >> operator fills the vacated leftmost bits with zeros. If exp1 is signed, then >> fills the leftmost bits with ones (if the sign bit is 1) or zeros (if the sign bit is 0). In other words, if exp1 is signed, the two bit-shift operators preserve its sign.
| NOTE | Not all compilers preserve the sign bit when doing bit-shift operations on signed integers. The K&R language definition and the ANSI standard make this behavior implementation-defined. |
Make sure that the right operand is not larger than the size of the object being shifted. For example, the following produces unpredictable and nonportable results because ints have fewer than 50 bits:
10 >> 50You will also get nonportable results if the shift count (the second operand) is a negative value.
| bit x of op1 | bit x of op2 | bit x of result |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
The following table shows an example of the bitwise AND operator:
| Expression | Hexadecimal Value | Binary Representation |
|---|---|---|
| 9430
5722 |
0x24D6
0x165A |
00100100 11010110
00010110 01011010 |
| 9430 & 5722 | 0x0452 | 00000100 01010010 |
| bit x of op1 | bit x of op2 | bit x of result |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
The bitwise inclusive OR operator (|) places a 1 in the resulting
value's bit position if either operand has a bit set at the position.
| Expression | Hexadecimal Value | Binary Representation |
|---|---|---|
| 9430
5722 |
0x24D6
0x165A |
00100100 11010110
00010110 01011010 |
| 9430 | 5722 | 0x36DE | 00110110 11011110 |
| bit x of op1 | bit x of op2 | bit x of result |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
The bitwise exclusive OR (XOR) operator (^) sets a bit in the resulting
value's bit position if either operand (but not both) has a bit set at
the position.
| Expression | Hexadecimal Value | Binary Representation |
|---|---|---|
| 9430
5722 |
0x24D6
0x165A |
00100100 11010110
00010110 01011010 |
| 9430 ^ 5722 | 0x328C | 00110010 10001100 |
| bit x of op2 | result |
|---|---|
| 0 | 0 |
| 0 | 1 |
The bitwise complement operator (~) reverses each bit in the operand:
| Expression | Hexadecimal Value | Binary Representation |
|---|---|---|
| 9430 | 0x24d6 | 00100100 11010110 |
| ~9430 | 0xdb29 | 11011011 00101001 |
int y = 5; float x;The following cast operation casts the value of y to float:
x = (float) y; /* x now equals 5.0 */Here are four more casts (assume that j is a scalar data type):
i = (float) j; /* Cast j's value to float */ i = (char *)j; /* Cast j's value to a pointer to a char */ i = ((int *)())j;/* Cast j's value to a pointer to a function returning an int */ i = (float) (double) j; /* Cast j's value first to a double and then to a float */It is important to note that if exp is a variable, a cast does not change this variable's data type; it only changes the type of the variable's value for that one expression. For instance, in the preceding casting examples, the cast does not produce any permanent effect on variable j.
There are no restrictions on casting from one scalar data type to another, except that you may not cast a void object to any other type. You should be careful when casting integers to pointers. If the integer value does not represent a valid address, the results are unpredictable.
A cast expression may not be an lvalue.
| Original Type | char | short | int | unsigned char | unsigned short | unsigned int |
|---|---|---|---|---|---|---|
| char | A | B | B | D | E | E |
| short | C | A | B | C | D | E |
| int (long)* | C | C | A* | C | C | D* |
| unsigned char | D | B | B | A | B | B |
| unsigned short | C | D | B | C | A | B |
| unsigned int | C | C | D | C | C | A |
* Case C for long in 64-bit mode.
hex dec char i = 37 55 (short) i => 0037 55 (int) i => 00000037 55 char j = c3 -61 (short) j => ffc3 -61 (int) j => ffffffc3 -61 unsigned char k = 37 55 (short) k => 0037 55 (int) k => 00000037 55
hex dec signed long int i = cf34bf1 217271281 (signed short int)i => 4bf1 19441 (signed char)i => f1 -15 (unsigned char)i => f1 241If, after casting to a signed type, the leftmost bit is 1, then the number is negative. However, if you cast to an unsigned type and after the shortening the leftmost bit is 1, then that 1 is part of the value (it is not the sign bit).
hex dec hex dec signed int i = fffffca9 -855 0000f2a1 62113 (unsigned int)i => fffffca9 4294966441 0000f2a1 62113The hexadecimal notation shows that the numbers are the same internally, but the decimal notation shows that the compiler interprets them differently.
hex dec signed short int i = ff55 -171 (unsigned long int)i => fffff55 4294967125
Here are some more examples:
float f = 3.700, f2 = -502.2, f3 = 7.35e9; (int)f => 3 (unsigned int)f => 3 (char)f => 3 (int)f2 => -502 in decimal fffffe0a in hex (unsigned int)f2 => 4294966794 in decimal or fffffe0a in hex (char)f2 => 10 in decimal 0a in hex (int)f3 => run-time error (unsigned int)f3 => run-time error (char)f3 => run-time error
| NOTE | Converting a large float to a char produces unpredictable results if the rounded value cannot fit in one byte. If the value cannot fit in four bytes, the run-time system issues an overflow error. |
int *int_p; float *float_p; struct S *str_p; extern foo(struct T *); . . . int_p = (int *) float_p; float_p = (float *) str_p; foo((struct T *) str_p);The cast is required whenever you assign a pointer value to a pointer variable that has a different base type, and when you pass a pointer value as a parameter to a function that has been prototyped with a different pointer type. The only exception to this rule concerns the generic pointer. You may assign any pointer value to a generic pointer without casting.
for (i = 10, j = 4; i * j < n; i++, j++);In the preceding example, the comma operator allows you to initialize both i and j at the beginning of the loop. The comma operator also allows you to increment i and j together at the end of each loop iteration.
All expressions return values. When you use a comma operator, the expression returns the value of the rightmost expression. For example, the following statement sets variable j to 2:
j = (x = 1, y = 2);Assignments such as these, however, are considered poor programming style. You should confine use of the comma operator to for loops.
if (exp1) exp2; else exp3;When a conditional expression is executed, exp1 is evaluated first. If it is true (that is, nonzero) exp2 is evaluated and its result is the value of the conditional expression. If exp1 is false, exp3 is evaluated and its result is the value of the conditional expression.
There is no requirement that you put parentheses around the exp1 portion of the conditional expression, but doing so will improve your code's readability.
Both exp2 and exp3 must be assignment-compatible. If exp2
and exp3 are pointers to different types, then the compiler issues
a warning. The value of a conditional expression is either exp2
or exp3, whichever is selected. The other expression is not evaluated.
The type of the result is the type that would be produced if exp2
and exp3 were mixed in an expression. For instance, if exp2
is a char and exp3 is a double, the result type will be double regardless
of whether
exp2 or exp3 is selected.
/* Program name is "conditional_exp_op_example".
This program uses the conditional expression to
see if the user wants to continue adding
numbers. */
include <stdio.h>
int main(void)
{
int a, b, c, d, again, total;
char answer;
printf("\n");
again = 1;
while (again)
{
printf("Enter four numbers separated by spaces that\n");
printf("you want added together: ");
scanf("%d %d %d %d", &a, &b, &c, &d);
fflush(stdin);
total = a + b + c + d;
printf("\nThe total is: %d\n", total);
printf("Do you want to continue ? ");
scanf("%c", &answer);
again = (answer == "y" || answer == "Y") ? 1 : 0;
} /* end while */
}
If you execute this program, you get the following output:
Enter four numbers separated by spaces that you want added together: 20 30 40 50 The total is: 140 Do you want to continue ? y Enter four numbers separated by spaces that you want added together: 1 2 3 4 The total is: 10 Do you want to continue ? n
postfix-expression ( [argument-expression-list] )
The postfix-expression must have the type "pointer to function returning T". The result of the function will be type T. Functions can return any type of object except array and function. Specifically, functions can return structures. In the case of structures, the contents of the returned structure is copied to storage in the calling function. For large structures, this can use a lot of execution time.
Although the expression denoting the called function must actually be a pointer to a function, in typical usage, it is simply a function name. This works because the function name will automatically be converted to a pointer.
C has no call statement. Instead, all function references must be followed by parentheses. The parentheses contain any arguments that are passed to the function. If there are no arguments, the parentheses must still remain. The parentheses can be thought of as a postfix call operator.
If the function name is not declared before it is used, the compiler enters the default declaration:
extern int identifier();
If the function being called has a prototype, each argument is evaluated and converted as if being assigned to an object of the type of the corresponding parameter. If the prototype has an ellipsis, any argument specified after the fixed parameters is subject to the default argument promotions described below.
The compiler checks to see that there are as many arguments as required by the function prototype. If the prototype has an ellipsis, additional parameters are allowed. Otherwise, they are flagged erroneous. Also, the types of the arguments must be assignment-compatible with their corresponding formal parameters, or the compiler will emit a diagnostic message.
If the function does not have a prototype, then the arguments are evaluated and subjected to the default argument promotions; that is, arguments of type char or short (both signed and unsigned) are promoted to type int, and float arguments are promoted to double.
In this case, the compiler does not do any checking between the argument types and the types of the parameters of the function (even if it has seen the definition of the function). Thus, for safety, it is highly advisable to use prototypes wherever possible.
In both cases, arrays of type T are converted to pointers to
type T, and functions are converted to pointers to functions.
void swap(int *x,int *y)
{
int t;
t = *x;
*x = *y;
*y = t;
}
To swap the contents of integer variables i and j, you
call the function as follows:
swap(&i, j);Notice that the addresses of the objects (pointers to int) were passed and not the objects themselves.
Because arrays of type T are converted into pointers to type T, you
might think that arrays are passed to functions using call by reference.
This is not actually the case. Instead, the address of the first element
is passed to the called function. This is still strictly call by value
since the pointer is passed by value. Inside the called function, references
to the array via the passed starting address, are actually references to
the array in the calling function. Arrays are not copied into the address
space of the called function.
The increment and decrement operators are unary. The operand must be
a scalar lvalue - it is illegal to increment or decrement a constant, structure,
or union. It is legal to increment or decrement pointer variables, but
the meaning of adding 1 to a pointer is different from adding 1 to an arithmetic
value. This is described in Pointer Operators
(*,
->, &).
The postfix increment and decrement operators fetch the current value of the variable and store a copy of it in a temporary location. The compiler then increments or decrements the variable. The temporary copy, which has the variable's value before it was modified, is used in the expression.
In many cases, you are interested only in the side effect, not in the result of the expression. In these instances, it doesn't matter whether you use postfix or prefix.
You need to be careful, however, when you use the increment and decrement
operators within an expression.
x++;is equivalent to
++x;Similarly, the statement
for (j = 0; j <= 10; j++)is equivalent to
for (j = 0; j <= 10; ++j)
#include <stdio.h>
void break_line(int interval)
{
int c, j=0;
while ((c = getchar()) != "\n") {
if ((j++ % interval) == 0)
printf("\n");
putchar(c);
}
}
This works because the postfix increment operator is used. If you use the
prefix increment operator, the function breaks the first line one character
early.
x = j * j++;The C language does not specify which multiplication operand is to be evaluated first. One compiler may evaluate the left operand first, while another evaluates the right operand first. The results are different in the two cases. If j equals 5, and the left operand is evaluated first, the expression will be interpreted as
x = 5 * 5; /* x is assigned 25 */If the right operand is evaluated first, the expression becomes
x = 6 * 5; /* x is assigned 30 */Statements such as this one are not portable and should be avoided. The side effect problem also crops up in function calls because the C language does not guarantee the order in which arguments are evaluated. For example, the function call
f(a, a++)is not portable because compilers are free to evaluate the arguments in any order they choose.
To prevent side effect bugs, follow this rule: If you use a side effect operator in an expression, do not use the affected variable anywhere else in the expression. The ambiguous expression above, for instance, can be made unambiguous by breaking it into two assignments:
x = j * j; ++j;
--j++is evaluated as
--(j++)This expression is illegal because j++ is not an lvalue as required by the operator. In general, you should avoid using multiple increment or decrement operators together.
i=k--; /* Stores the value of k in i then decrements k. */ j=l++; /* Stores the value of l in j then increments l. */ i=--k; /* Decrements k then stores the new value of k in i. */ j=++l; /* Increments l then stores the new value of l in j. */The following example uses both prefix and postfix increment and decrement operators:
#include <stdio.h>
int main(void)
{
int j = 5, k = 5, l = 5, m = 5;
printf("j: %d\t k: %d\n", j++, k--);
printf("j: %d\t k: %d\n", j, k);
printf("l: %d\t m: %d\n", ++l, --m);
printf("l: %d\t m: %d\n", l, m);
}
The result is as follows:
j: 5 k: 5 j: 6 k: 4 l: 6 m: 4 l: 6 m: 4The results show that the initial values of j and k are used in the first printf(). They also show that l and m are incremented and decremented, respectively, before the third printf() call.
(x < y) && (y < z)The logical negation operator (!) takes only one operand. If the operand is true, the result is false; if the operand is false, the result is true.
The operands to the logical operators may be integers or floating-point objects. The expression
1 && -5results in 1 because both operands are nonzero. The same is true of the expression
0.5 && -5Logical operators (and the comma and conditional operators) are the only operators for which the order of evaluation of the operands is defined. The compiler must evaluate operands from left to right. Moreover, the compiler is guaranteed not to evaluate an operand if it is unnecessary. For example, in the expression
if ((a != 0) && (b/a == 6.0))if a equals 0, the expression (b/a == 6) will not be evaluated. This rule can have unexpected consequences when one of the expressions contains side effects.
| Operand | Operator | Operand | Result |
|---|---|---|---|
| zero | && | zero | 0 |
| nonzero | && | zero | 0 |
| zero | && | nonzero | 0 |
| nonzero | && | nonzero | 1 |
| zero | || | zero | 0 |
| nonzero | || | zero | 1 |
| zero | || | nonzero | 1 |
| nonzero | || | nonzero | 1 |
| not applicable | ! | zero | 1 |
| ! | nonzero | 0 |
| Given the following declarations:
int j = 0, m = 1, n = -1; float x = 2.5, y = 0.0; |
||
|---|---|---|
| Expression | Equivalent Expression | Result |
| j && m | (j) && (m) | 0 |
| j < m && n < m | (j < m) && (n < m) | 1 |
| m + n || ! j | (m + n) || (!j) | 1 |
| x * 5 && 5 || m / n | ((x * 5) && 5) || (m / n) | 1 |
| j <= 10 && x >= 1 && m | ((j <= 10) && (x >= 1)) && m | 1 |
| !x || !n || m+n | ((!x) || (!n)) || (m+n) | 0 |
| x * y < j + m || n | ((x * y) < (j + m)) || n | 1 |
| (x > y) + !j || n++ | ((x > y) + (!j)) || (n++) | 1 |
| (j || m) + (x || ++n) | (j || m) + (x || (++n)) | 2 |
if ((a < b) && (c == d))The system begins by evaluating (a < b). If a is not less than b, the system knows that the entire expression is false, so it will not evaluate (c == d). This can cause problems if some of the expressions contain side effects:
if ((a < b) && (c == d++))In this case, d is only incremented when a is less than b. This may or may not be what the programmer intended. In general, you should avoid using side effect operators in logical expressions.
/* Program name is "logical_ops_example". This program */
/* shows how logical operators are used. */
#include <stdio.h>
int main(void)
{
int won_lottery, enough_vacation, money_saved;
char answer;
won_lottery = enough_vacation = money_saved = 0;
printf("\nThis program determines whether you can ");
printf("take your next vacation in Europe.\n");
printf("Have you won the lottery? y or n: ");
fflush(stdin);
scanf("%c", &answer);
if (answer == "y")
won_lottery = 1;
printf("Do you have enough vacation days saved? \
y or n: ");
fflush(stdin);
scanf ("%c", &answer);
if (answer == "y")
enough_vacation = 1;
printf("Have you saved enough money for the trip? \
y or n: ");
fflush(stdin);
scanf("%c", &answer);
if (answer == "y")
money_saved = 1;
printf("\n");
if (won_lottery)
{
printf("Why do you need a program to decide if you");
printf(" can afford a trip to Europe?\n");
} /* end if */
if (won_lottery || (enough_vacation &&money_saved))
printf("Look out Paris!\n");
else if (enough_vacation &&(!money_saved))
printf("You"ve got the time, but you haven't got \
the dollars.\n");
else if (!enough_vacation || (!money_saved))
{
printf("Tough luck. Try saving your money and ");
printf("vacation days next year.\n");
} /* end else/if */
}
If you execute this program, you get the following output:
This program determines whether you can take your next vacation in Europe. Have you won the lottery? y or n: y Do you have enough vacation days saved? y or n: n Have you saved enough money for the trip? y or n: n Why do you need a program to decide if you can afford a trip to Europe? Look out Paris!
long *ptr;The data type, long in this case, refers to the type of variable that ptr can point to. To assign a pointer variable with the virtual address of a variable, you can use the address-of operator &. For instance, the following is legal:
long *ptr; long long_var; ptr = &long_var; /* Assign the address of long_var to ptr. */But this is illegal:
long *ptr; float float_var; ptr = &float_var; /* ILLEGAL - because ptr can only store the address of a long int. */The following program illustrates the difference between a pointer variable and an integer variable.
/* Program name is "ptr_example1". */
#include <stdio.h>
int main()
{
int j = 1;
int *pj;
pj = &j; /* Assign the address of j to pj */
printf("The value of j is: %d\n", j);
printf("The address of j is: %p\n", pj);
}
If you run this program (in 32-bit mode), the output looks something like
this:
The value of j is: 1 The address of j is: 7b033240
/* Program name is "ptr_example2". */
#include <stdio.h>
int main(void)
{
char *p_ch;
char ch1 = "A", ch2;
printf("The address of p_ch is %p\n", &p_ch);
p_ch = &ch1;
printf("The value of p_ch is %p\n", p_ch);
printf("The dereferenced value of p_ch is %c\n",
*p_ch);
}
The output from this program looks something like this:
The address of p_ch is 7b033240 The value of p_ch is 7b033244 The dereferenced value of p_ch is AThis is a roundabout and somewhat contrived example that assigns the character A to both ch1 and ch2. It does, however, illustrate the effect of the dereference (*) operator. The variable ch1 is initialized to A. The first printf() call displays the address of the pointer variable p_ch. In the next step, p_ch is assigned the address of ch1, which is also displayed. Finally, the dereferenced value of p_ch is displayed and ch2 is assigned to it.
The expression *p_ch is interpreted as "Take the address value stored in p_ch and get the value stored at that address." This gives us a new way to look at the declaration. The data type in the pointer declaration indicates what type of value results when the pointer is dereferenced. For instance, the declaration
float *fp;means that when *fp appears as an expression, the result will be a float value.
The expression *fp can also appear on the left side of an expression:
*fp = 3.15;In this case, we are storing a value (3.15) at the location designated by the pointer fp. This is different from
fp = 3.15;which attempts to store the address 3.15 in fp. This, by the way, is illegal, because addresses are not the same as floating-point values.
When you assign a value through a dereferenced pointer, make sure that the data types agree. For example:
/* Program name is "ptr_example3". */
#include <stdio.h>
int main(void)
{
float f = 1.17e3, g;
int *ip;
ip = &f;
g = *ip;
printf("The value of f is: %f\n", f);
printf("The value of g is %f\n", g);
}
The result is
The value of f is: 1170.000000 The value of g is: 1150435328.000000In the preceding example, instead of getting the value of f, g gets an erroneous value because ip is a pointer to an int, not a float. The HP C compiler issues a warning message when a pointer type is unmatched. If you compile the preceding program, for instance, you receive the following message:
cc: "ptr_example3.c", line 9: warning 604: Pointers are not assignment-compatible.
When you add or subtract an integer to or from a pointer, the compiler automatically scales the integer to the pointer's type. In this way, the integer always represents the number of objects to jump, not the number of bytes. For example, consider the following program fragment:
int x[10], *p1x = x, *p2x; p2x = p1x + 3;Since pointer p1x points to a variable (x) that is 4 bytes long, then the expression p1x + 3 actually increments p1x by 12 (4 * 3), rather than by 3.
It is legal to subtract one pointer value from another, provided that the pointers point to the same type of object. This operation yields an integer value that represents the number of objects between the two pointers. If the first pointer represents a lower address than the second pointer, the result is negative. For example,
&a[3] - &a[0]evaluates to 3, but
&a[0] - &a[3]evaluates to -3.
It is also legal to subtract an integral value from a pointer value. This type of expression yields a pointer value. The following examples illustrate some legal and illegal pointer expressions:
long *p1, *p2; int a[5], j; char *p3; p1 = a; /* Same as p1 = &a[0] */ p2 = p1 + 4; /* legal */ j = p2 - p1; /* legal -- j is assigned 4 */ j = p1 - p2; /* legal -- j is assigned -4 */ p1 = p2 - 2; /* legal -- p2 points to a[2] */ p3 = p1 - 1; /* ILLEGAL -- different pointer types*/ j = p1 - p3; /* ILLEGAL -- different pointer types*/ j = p1 + p2; /* ILLEGAL -- can't add pointers */
int i, *p = &i; func((char *) p);As a second example, a pointer to a char is cast to a pointer to struct H:
struct H {
int q;
} x, y;
char *genp = &x;
y = (struct H *)genp->q;
See Cast Operator for more information about
the cast operator.
It is always legal to assign any pointer type to a generic pointer, and vice versa, without a cast. For example:
float x, *fp = &x; int j, *pj = &j; void *pv; pv = fp; /* legal */ fp = pv; /* legal */In both these cases, the pointers are implicitly cast to the target type before being assigned.
char *p; p = 0; /* make p a null pointer */In this one case - assignment of 0 - you do not need to cast the integral expression to the pointer type.
Null pointers are particularly useful in control-flow statements, since the zero-valued pointer evaluates to false, whereas all other pointer values evaluate to true. For example, the following while loop continues iterating until p is a null pointer:
char *p;
. . .
while (p) {
. . .
/* iterate until p is a null pointer */
. . .
}
This use of null pointers is particularly prevalent in applications that
use arrays of pointers.
The compiler does not prevent you from attempting to dereference a null pointer; however, doing so may trigger a run-time access violation. Therefore, if it is possible that a pointer variable is a null pointer, you should make some sort of test like the following when dereferencing it:
if (px && *px) /* if px = 0, expression will short-circuit . . . before dereferencing occurs*/Null pointers are a portable feature.
/* Program name is "pointer_array_example1". This program
* shows how to access a 1-dimensional array through
* pointers. Function count_chars returns the number of
* characters in the string passed to it.
* Note that *arg is equivalent to a_word[0];
* arg + 1 is equivalent to a_word[1]...
*/
#include <stdio.h>
int count_chars(char *arg)
{
int count = 0;
while (*arg++)
count++;
return count;
}
int main(void)
{
char a_word[30];
int number_of_characters;
printf("Enter a word -- ");
scanf("%s", a_word);
number_of_characters = count_chars(a_word);
printf("%s contains %d characters.\n", a_word,
number_of_characters);
}
If you execute this program, you get the following output:
Enter a word -- Marilyn Marilyn contains 7 characters.
/* Program name is "pointer_array_example2". This program
* demonstrates two ways to access a 2-dimensional array.
*/
#include <stdio.h>
int main(void)
{
int count = 0, which_name;
char c1, c2;
static char str[5][10] = {"Phil", "Sandi", "Barry",
"David", "Amy"};
static char *pstr[5] = { str[0], str[1], str[2],
str[3], str[4]};
/* pstr is an array of pointers. Each element in the array
* points to the beginning of one of the arrays in str.
*/
/* Prompt for information. */
printf("Which name do you want to retrieve?\n");
printf("Enter 0 for the first name,\n");
printf(" 1 for the second name, etc. -- ");
scanf("%d", &which_name);
/* Print name directly through array. */
while (c1 = str[which_name][count++])
printf("%c", c1);
printf("\n");
/* Print same name indirectly through an array of pointers. */
while (c2 = *(pstr[which_name]++))
printf("%c", c2);
/* We could also have used the following statement instead of
* the two previous ones: printf("%s", pstr[which_name]);
*/
printf("\n");
}
If you execute this program, you get the following output:
Which name do you want to retrieve? Enter 0 for the first name, 1 for the second name, etc. -- 1 Sandi Sandi
The equality operator (==) performs the same function as Pascal's
=
or FORTRAN's .EQ.; it just looks different. Although the equality
operator looks similar to the assignment operator (=), the two
operators serve completely different purposes. Use the assignment operator
when you want to assign a value to a variable, but use the equality operator
when you want to test the value of an expression.
while (j = 5) do_something();What is intended, clearly, is that the do_something() function should only be invoked if j equals five. It should be written
while (j == 5) do_something();The first version is syntactically legal, since all expressions have a value. The value of the expression j = 5 is 5. Since this is a nonzero value, the while expression will always evaluate to true and do_something() will always be invoked.
a + b * c < d / fis evaluated as if it had been written
(a + (b * c)) < (d / f)Among the relational operators, >, >=, <, and <= have the same precedence. The == and != operators have lower precedence. All of the relational operators have left-to-right associativity. The following table illustrates how the compiler parses complex relational expressions.
| Given the following declaration:
int j = 0, m = 1, n = -1; float x = 2.5, y = 0.0; |
||
|---|---|---|
| Expression | Equivalent Expressions | Result |
| j > m | j > m | 0 |
| m / n < x | (m / n) < x | 1 |
| j <= m >= n | ((j <=m) >= n) | 1 |
| j <= x == m | ((j <= x) == m) | 1 |
| - x + j == y > n > m | ((-x) + j) == ((y > n) >= m) | 0 |
| x += (y >= n) | x = (x + (y >= n)) | 3.5 |
| ++j == m != y * 2 | ((++j) == m) != (y * 2) | 1 |
The value of a relational expression is an integer, either 1 (indicating
the expression is true) or 0 (indicating the expression is false). The
examples in the following table illustrate how relational expressions are
evaluated:
| Expression | Value |
|---|---|
| -1 < 0 | 1 |
| 0 > 1 | 0 |
| 5 == 5 | 1 |
| 7 != -3 | 1 |
| 1 >= -1 | 1 |
| 1 > 10 | 0 |
Because Boolean values are represented as integers, you can write
if (j) statement;If j is any nonzero value, statement is executed; if j equals 0, statement is skipped. Likewise, the statement
if (isalpha(ch))is exactly the same as
if (isalpha(ch) != 0)The practice of using a function call as a Boolean expression is a common idiom in C. It is especially effective for functions that return 0 if an error occurs, since you can use a construct such as
if (func()) proceed; else error handler;
(1.0/3.0 + 1.0/3.0 + 1.0/3.0) == 1.0This evaluates to 0 (false) because the fraction 1.0/3.0 contains an infinite number of decimal places (3.33333...). The computer is only capable of holding a limited number of decimal places, so it rounds each occurrence of 1/3. As a result, the left side of the expression does not equal exactly 1.0.
This problem can occur in even more subtle ways. Consider the following code:
double divide(double num, double denom)
{
return num/denom;
}
int main(void)
{
double c, a = 1.0, b = 3.0;
c = a/b;
if (c != divide(a, b))
printf("Fuzzy doubles\n");
}
Surprisingly, the value stored in c may not equal the value returned
by divide(). This anomaly occurs due to the fact that some computers
can represent more decimal places for values stored in registers than for
values stored in memory. Because the value returned by divide() is never
stored in memory, it may not be equal to the value c, which has
been rounded for memory storage.
| NOTE | To avoid bugs caused by inexact floating-point representations, you should refrain from using strict equality comparisons with floating-point types. |
/* Program name is "relational_example". This program
* does some mathematical calculations and shows
* C's relational operators in action.
*/
#include <stdio.h>
int main(void)
{
int num, i;
printf("\n");
num = 5;
printf("The number is: %d\n", num);
for (i = 0; i <= 2; i++)
{
if (num < 25)
{
num *= num;
printf("The number squared is: %d\n", num);
}
else if (num == 25) {
num *= 2;
printf("Then, when you double that, you get: %d\n", num);
}
else if (num > 25)
{
num -= 45;
printf("And when you subtract 45, you"re back where ");
printf("you started at: %d\n", num);
} /* end if */
} /* end for */
if (num != 5)
printf("The programmer made an error in setting up this \
example\n");
}
If you execute this program, you get the following output:
The number is: 5 The number squared is: 25 Then, when you double that, you get: 50 And when you subtract 45, you're back where you started at: 5
If the operand is an expression, sizeof returns the number of bytes that the result occupies in memory:
/* Returns the size of an int (4 if ints are four * bytes long) */ sizeof(3 + 5) /* Returns the size of a double (8 if doubles are * eight bytes long) */ sizeof(3.0 + 5) /* Returns the size of a float (4 if floats are * four bytes long) */ float x; sizeof(x)For expressions, the parentheses are optional, so the following is legal:
sizeof xBy convention, however, the parentheses are usually included.
The operand can also be a data type, in which case the result is the length in bytes of objects of that type:
sizeof(char) /* 1 on all machines */ sizeof(short) /* 2 on HP 9000 Series */ sizeof(float) /* 4 on HP 9000 Series */ sizeof(int *) /* 4 on HP 9000 Series */The parentheses are required if the operand is a data type.
| NOTE | The results of most sizeof expressions are implementation dependent. The only result that is guaranteed is the size of a char, which is always 1. |
In general, the sizeof operator is used to find the size of
aggregate data objects such as arrays and structures.
/* Program name is "sizeof_example". This program
* demonstrates a few uses of the sizeof operator.
*/
#include <stdio.h>
int main(void)
{
printf("TYPE\t\tSIZE\n\n");
printf("char\t\t%d\n", sizeof(char));
printf("short\t\t%d\n", sizeof(short));
printf("int\t\t%d\n", sizeof(int));
printf("float\t\t%d\n", sizeof(float));
printf("double\t\t%d\n", sizeof(double));
}
If you execute this program, you get the following output:
TYPE SIZE char 1 short 2 int 4 float 4 double 8
postfix-expression . identifier postfix-expression -> identifier
The postfix-expression in the first alternative must be a structure or a union. The expression is followed by a period (.) and an identifier. The identifier must name a member defined as part of the structure or union referenced in the postfix-expression. The value of the expression is the value of the named member. It is an lvalue if the postfix-expression is an lvalue.
If the postfix-expression is a pointer to a structure or a pointer to a union, follow it with an arrow (composed of the - character followed by the |) and an identifier. The identifier must name a member of the structure or union which the pointer references. The value of the primary expression is the value of the named member. The resulting expression is an lvalue.
The . operator and the -> operator are closely related.
If S is a structure, M is a member of structure S,
and &S is a valid pointer expression, S.M is the
same as (&S)->M.
The following lists C operator precedence in highest to lowest
precedence:
| Class of operator | Operators | Grouping |
|---|---|---|
| primary | () [] -> . | left to right |
| unary | (type casting)
sizeof & (address of) * (dereference) - (reverse sign) ~ ! ++ -- |
right to left |
| multiplicative | * / % | left to right |
| additive | + - | left to right |
| shift | << >> | left to right |
| relational | < <= > >= | left to right |
| equality | == != | left to right |
| bitwise AND | & | left to right |
| bitwise XOR | ^ | left to right |
| bitwise OR | | | left to right |
| logical AND | && | left to right |
| logical OR | || | left to right |
| conditional | ?: | right to left |
| assignment | = += -= *= /= %= >>= <<= &= ^= |= | right to left |
| comma | , | left to right |
a/b * c/dbehaves as if it had been written:
(((a/b)*c)/d)Likewise, an operator that groups from the right to the left causes the expression
a = b = cto behave as if it had been written:
a = (b = c)
| Symbol | Meaning |
|---|---|
| ! | logical negation |
| != | inequality |
| % | remainder |
| & | AND (bitwise) and address-of |
| && | AND (logical) |
| () | cast and function call |
| * | multiplication and indirection |
| + | addition and unary plus |
| ++ | increment, prefix or postfix |
| , | comma |
| - | subtraction and unary minus |
| -- | decrement, prefix or postfix |
| -> | structure/union pointer (arrow) |
| . | structure/union member (dot) |
| / | division |
| < | less-than |
| << | left-shift |
| <= | less-than-or-equal-to |
| = | assignment |
| == | equality |
| > | greater-than |
| >= | greater-than-or-equal-to |
| >> | right shift |
| ?: | conditional |
| [ ] | subscript |
| ^ | OR (bitwise exclusive) |
| | | OR (bitwise inclusive) |
| || | OR (logical) |
| ~ | complement |
| op= | assignment, compound |
| sizeof | compute object size at translation-time |
5 5 + 6 * 13 / 3.0 "a"
j j * k j / k + 3 k - "a" 3 + (int) 5.0
x x + 3 x / y * 5 3.0 3.0 - 2 3 + (float) 4
For example, all simple variables, like ints and floats are lvalues. An element of an array is also an lvalue; however an entire array is not. A member of a structure or union is an lvalue; an entire structure or union is not.
Given the following declarations:
int *p, a, b; int arr[4]; int func(); a /* lvalue */ a + b /* Not an lvalue */ p /* lvalue */ *p /* lvalue */ arr /* lvalue, but not modifiable */ *(arr + a) /* lvalue */ arr[a] /* lvalue, equivalent to *(arr+a) */ func /* Not an lvalue */ func() /* Not an lvalue */
p &j p + 1 "abc" (char *) 0x000fffff
x = 6;The value 6 is both the byproduct value and the value that gets assigned to x. The byproduct value is not used.
The following example uses the byproduct value:
y = x = 6;The equals operator binds from right to left; therefore, C first evaluates the expression x = 6. The byproduct of this operation is 6, so C sees the second operation as
y = 6Now, consider the following relational operator expression:
(10 < j < 20)It is incorrect to use an expression like this to find out whether j is between 10 and 20. Since the relational operators bind from left to right, C first evaluates
10 < jThe byproduct of a relational operation is 0 if the comparison is false and 1 if the comparison is true.
Assuming that j equals 5, the expression 10 < j is false. The byproduct will be 0. Thus, the next expression evaluated:
0 < 20is true (or 1). This is not the expected answer when j equals 5.
Finally, consider the following fragment:
static char a_char, c[20] = {"Valerie"}, *pc = c;
while (a_char = *pc++) {
. . .
This while statement uses C's ability to both assign and test
a value. Every iteration of while assigns a new value to variable
a_char.
The byproduct of an assignment is equal to the value that gets assigned.
The byproduct value will remain nonzero until the end of the string is
reached. When that happens, the byproduct value will become 0 (false),
and the while loop will end.
b = ++a*2 + ++a*4;are dangerous. The language does not specify whether the variable a is first incremented and multiplied by 4 or is first incremented and multiplied by 2. The value of this expression is undefined.