PowerPoint Presentation
CSE 2421
The C Language – Part 2
Required Reading:
Computer Systems: A Programmer’s Perspective, 3rd Edition,
Chapter 2 thru Section 2.1.2
Modern computers store and process information as two-valued signals.
– Bryant & O’Hallaron, Computer Systems: A Programmer’s Perspective, 3rd Edition
You can think of these two signals as off/on or true/false or 0/1, etc.
In isolation, a single bit is not very useful. When we group bits together and apply some interpretation that gives meaning to the different possible bit patterns, however, we can represent the elements of any finite set.
– Bryant & O’Hallaron, Computer Systems: A Programmer’s Perspective, 3rd Edition
Note that this means a particular bit pattern could have several different interpretations depending upon its context.
Representing Data
Most of the time, binary (0s and 1s) data is represented in blocks of 8 digits at a time since this is the smallest addressable unit.
e.g. 1 byte (8 bits), 2 bytes (16 bits), etc.
For the next few slides and for other select times when we want to discuss individual hexadecimal values, we will be using binary data in blocks of 4 digits.
When discussing 8 bits at a time, we call it a byte; when discussing 4 bits at a time, it is called a nibble.
Representing Data
Integer Decoding
Binary to Decimal
Unsigned = simple binary = B2U
All digits represent a positive power of 2
NO NEGATIVE NUMBERS
0101 = 0*23+1*22+0*21+1*20 = 5
1111 = 1*23+1*22+1*21+1*20 = 15
1110 = 1*23+1*22+1*21+0*20 = 14
1001 = 1*23+0*22+0*21+1*20 = 9
Binary to Hexadecimal
Unsigned = simple binary = B2U
All digits represent a positive power of 2
NO NEGATIVE NUMBERS
0101 = 0*23+1*22+0*21+1*20 = 5
1111 = 1*23+1*22+1*21+1*20 = F
1110 = 1*23+1*22+1*21+0*20 = E
1001 = 1*23+0*22+0*21+1*20 = 9
Memorize how to “pattern match” binary values to decimal and hexadecimal values to make things easier for yourself
BINARY
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111
B2U
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
HEX
0
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F
Integer Decoding
Binary to Decimal
Signed = two’s complement = B2T
Most significant digit (left-most bit) represents the sign bit
Remainder of digits represent positive powers of 2
ANY POSITIVE NUMBER: SAME AS B2U
1111 = -1*23+1*22+1*21+1*20 = -8 + 7 = -1
1110 = -1*23+1*22+1*21+0*20 = -8 + 6 = -2
1001 = -1*23+0*22+0*21+1*20 = -8 + 1 = -7
Another way, if sign bit = 1, then it’s a negative number and to get the magnitude of that number, invert all bits and add 1, make sign negative
1010 changes to 0101+1 = 0110
0110 = 6, so final value is -6
Note: hexadecimal values do not change. The values for 1000 through 1111 are 8 through F.
BINARY
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111
B2U
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
B2T
0
1
2
3
4
5
6
7
-8
-7
-6
-5
-4
-3
-2
-1
HEX
0
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F
Hexadecimal Integers
Large binary numbers are cumbersome to read, so hexadecimal digits offer a convenient way to represent binary data.
Each digit in a hexadecimal integer represents four binary bits.
Two hexadecimal digits together represent a byte
A single hexadecimal digit represents a decimal value between 0 and 15, so letters A to F represent decimal values in the range 10 through 15.
In hexadecimal each digit position represents a power of 16.
BINARY
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111
B2U
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
B2T
0
1
2
3
4
5
6
7
-8
-7
-6
-5
-4
-3
-2
-1
HEX
0
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F
Suppose you are given the hexadecimal number : 0x173A
Convert to binary format by expanding each hexadecimal digit, as follows:
Hexadecimal 1 7 3 A
Binary 0001 0111 0011 1010
This gives the binary representation:
0001011100111010
Putting spaces between each 4 digits for readability in this class is acceptable.
Converting Hexadecimal to Binary
Suppose you are given the binary number :
11110010101101
Starting from the right side, split the number into groups of 4 bits each (if the number of bits is not a multiple of 4, the leftmost group will have fewer than 4 bits):
Binary 11 1100 1010 1101
If the left-most group has fewer than 4 digits, add leading 0s, then pattern match each nibble with the appropriate hex digit.
Binary 0011 1100 1010 1101
Hexadecimal 3 C A D
This gives the hexadecimal representation:
0x3CAD
Converting Binary to Hexadecimal
8
Converting from decimal to another base
To convert from decimal to base b, divide the decimal number by b, and write the remainders (which will be between 0 and b – 1), until the quotient is zero.
Then, write the remainders in order from the last remainder obtained to the first remainder obtained.
Example:
convert (221)10 to base 2 (binary) (see the next slide)
Example: Convert (221)10 to binary
Quotient Remainder
2 221 1
2 110 0
55 1
2 27 1
2 13 1
2 6 0
2 3 1
2 1 1
0
Write the remainders in order from the last one obtained, to the first one obtained:
(221)10 = (11011101)2 = 0000 1101 1101
Thus, this expresses the binary representation of the original decimal number.
Why should we ultimately represent decimal 221 with 12 binary digits?
We need at least 8 to represent 221, but, when looking at the value in 8 binary digits, we really can’t tell whether we should be interpreting the value as unsigned or signed since the most significant bit is 1, so we should used AT LEAST 9 digits.
Using a number of digits to represent a value that is a multiple of 4, makes it easier to convert from binary to hex
Binary to Hex correspondence
4 binary digits can be converted to a single hex digit, as shown below.
Binary Hex Binary Hex
0000 0 1000 8
0001 1 1001 9
0010 2 1010 A
0011 3 1011 B
0100 4 1100 C
0101 5 1101 D
0110 6 1110 E
0111 7 1111 F
So how many hex digits does it take to represent 1 byte?
Example: Convert (743)10 to hexadecimal
Note: Since remainders can have values from 0 to b – 1, that is, 0 to 15 in this case, we use F for 15, E for 14, D for 13, C for 12, B for 11, and A for 10
Quotient Remainder
16 743 7
16 46 E
16 2 2
0
Therefore, if we write the remainders in order from the last one obtained to the first one obtained, we have:
(743)10 = (2E7)16
To convert from base b to decimal
We can do the conversion by multiplying the value of each digit, dm, by dm, for m from 0 (least significant digit) to n-1 (most significant digit) for an n digit number, and by summing all the products obtained in this way.
Example of conversion from base b to decimal
Problem: Convert (5377)8 to decimal.
Solution:
(5377)8 = 5 * 83 + 3 * 82 + 7 * 81 + 7 * 80
= 5 * 512 + 3 * 64 + 7 * 8 + 7 * 1
= 2,560 + 192 + 56 + 7
= 2,815
Example of conversion from base b to decimal
Problem: Convert (5377)16 to decimal.
Solution:
(5377)16 = 5 * 163 + 3 * 162 + 7 * 161 + 7 * 160
= 5 * 4096 + 3 * 256 + 7 * 16 + 7 * 1
= 20,480 + 768 + 112 + 7
= 21,367
These few slides give the basics.
We’ll get a bit more involved in the 2nd half of the semester
Number conversions
<< left-shift
>> right-shift
& bitwise AND
| bitwise OR
^ bitwise exclusive-OR
~ bitwise NOT (complement, not negation)
&& logical AND
|| logical OR Logical operators
! logical NOT
< less than
> greater than
<= less than or equal
>= greater than or equal
== equals
!= does not equal
Note 1: Logical operators evaluate an expression on each side of the operator, give that expression a value of 1(true) or 0 (false), then perform the logical operation on the whole.
Note 2: C has no such keywords as true or false, #defines are used
Note 3: ‘OR’, ‘AND’ , and ‘NOT’: There are differences between bitwise and logical operators.
Logical Operators
BITWISE OPERATORS:
0 is 0
1 is 1
LOGICAL OPERATORS:
0 is FALSE
Everything else is TRUE
Practical Result:
0 if false;
1 if true
relational operators
Bitwise operators
17
Does this remind anyone of Foundations I? It should.
Logical versus Bitwise Operators example
int a = 10;
int c = 0; int a = 10;
int c = 1; int a = 10;
int c = 3;
a & c
Bitwise a=10=1010b
c= 0=0000b
———–
0000b
010 a=10=1010b
c= 1=0001b
———–
0000b
010 a=10=1010b
c= 3=0011b
———–
0010b
210
a && c
Logical a=True
c=False
———–
False a=True
c=True
———–
True a=True
c=True
———–
True
18
When we represent values in binary, we can do what is called “shifting” bits either to the right or to the left.
Left shift example:
Binary value: 0111 0101
Left shift 2 places: 1101 0100 (fill with 0’s)
Bit shifting
Bit
Bucket
01
The Bit Bucket is a fictitious, but often used item
01
Shifting to the right has 2 options:
Arithmetic shift
Logical shift
Shift Right Arithmetic
Fills in from the left with copy of Most Significant Bit
Preserves the sign of the value
Used when interpreting the value as B2T
Binary value: 1111 0101
Shift Right Arithmetic 1 bit: 1111 1010
Shift Right Arithmetic 2 bits: 1111 1101
If the MSB had been 0, then new
bits would be 0s
Shift Right Logical
Fills in from the left with 0’s
Used when interpreting the binary value as B2U
Binary value: 1111 0101
Shift Right Logical 1 bit: 0111 1010
Shift Right Logical 2 bits: 0011 1101
Bit Shifting
Bit
Bucket
1
01
01
1
The Bit Bucket is a fictitious, but often used item
1
01
1
01
20
Used to compare two values
< <= > >= (Higher precedence than == and !=)
== !=
Precedence order is given above, L-R associativity
Arithmetic operators have higher precedence than relational operators
An expression that is true evaluates to a nonzero number (generally 1). A false expression evaluates to zero.
For example, the expression (0 == 2) evaluates to 0.
while the expression (2 == 2) evaluates to a 1
(non-zero technically, but usually 1).
Relational Operators
ANSI C does not have a distinct Boolean type
int is used instead (usually, but other types are possible)
0 is treated as FALSE
Non-zero is treated as TRUE
i = 0;
while (i – 10) {
…
}
As long as (i-10) ¹ 0 it is considered true, and the body of the while loop will execute.
(Later versions of C have Boolean type)
Boolean Operators
Short-Circuit Evaluation: Relational statements stop evaluating once a statement’s value is definitive
In (x && y), if 1st condition evaluates to false (e.g. if expression x==0), evaluation stops
It does not matter what the outcome of the y expression is because (x && y) will always evaluate to false. y is not evaluated or compared
Same for OR if first expression evaluates to 1 (true).
This can cause buggy code (or not!)
This is a valid way to write code
There are many arguments made that it can be a correct and expedient way to write some code
Be very cautious
Boolean Operators (cont)
Short-Circuit Evaluation:
func1(float a, float b){
float func_result = 0.0f;
If ((b !=0.0f) && (a/b< 0.5f)){
printf(“ The result of func1 is %f.4\n”, ((a*b) + (a/b)));
}
return;
}
In this example, short-circuit evaluation saves your bacon!
Without short-circuit, this code will seg fault when b=0.0f and a/b is computed.
NOTE: (b !=0.0f) and (a/b< 0.5f) are logical expressions and have values of true or false.
Boolean Operators (cont)
Short-Circuit Evaluation:
func1(int a, int b){
int func_result = 0;
If ((b ==0) && ((func_result = (++a*b+3)))){
printf(“ The result of func1 is %d\n”,a*func_result);
}
return;
In this example, short-circuit evaluation might cause you problems because the variable a and func_result sometimes change value in the 2nd expression.
NOTE: (b==0) and ((func_result = (++a*b+))) are logical expressions and have values of true or false.
Boolean Operators (cont)
Type casting – EXPLICIT
You purposely converting a variable from one data type to another data type in your code
Syntax: (type-name) variable
Type combination and promotion - IMPLICIT
(‘a’ – 32) = 97 – 32 = 65 (if used as a char = ‘a’)
Smaller type (char) is “promoted” to be the same size as the larger type (int), remember that constants default to int.
Determined at compile time – type of the whole expression is based purely on the types of the values in the expressions
Does not lose information – convert from type to compatible larger type
Whether the casting is implicit or explicit, the compiler will create separate storage for the cast value, and any operands that are necessary to determine it. [See next slide for example]
32
Arithmetic Type Issues
The usual arithmetic conversions are implicitly performed to cast values of distinct types to a common type.
-Compiler first performs integer promotion (promotion of char to int)
-short data type is ignored
-If operands still have different types, then any variables or constants in operand expressions are converted to the type that appears highest in the following hierarchy (except any variables that were already of that type; for those, no conversion is necessary)
C type-casting
The following code is supposed to scale a homework score in the range 0-20 to be in the range 0-100.
cnvt_score(){
int score;
/* score gets set in the range 0..20 */
score = (score / 20) * 100; /*convert to
percentage*/
return(score);
}
Does this work?
34
Arithmetic Expressions and Casting
This does not work! Unfortunately, score will almost always be set to 0 for this code because the integer division in the expression (score/20) will be 0 for every value of score less than 20.
The fix is to force the quotient to be computed as a floating point number...
score = ((double)score / 20) * 100; /*OK – double floating point division with explicit cast */
score = (score / 20.0) * 100; /*OK – double floating point division with implicit casting because float (double) constant 20.0 */
score = (int)(score / 20.0) * 100; /*NO -- the (int)cast truncates the floating quotient back to 0 if score < 20 */
35
Arithmetic Expressions and Casting
In class Exercise
#include
main()
{
int z, a=10, b=0, c=3;
z = (a > c) || (++a > b);
printf(“z = %d a = %d\n”, z , a); /* 1. z= ?? a=?? */
c = 30;
z = (a > c) || (++a > b);
printf(“z = %d a = %d\n”, z, a); /* 2. z= ?? a=?? */
a = 10;
c=3;
z = ++a * c + a++;
printf(“z = %d a = %d”,z,a); /* 3. z= ?? a=?? */
return 0;
}
/docProps/thumbnail.jpeg