See this page as a slide show
More on bases
And why are printf
and scanf
so complicated?
0xdeadbeef
0666
01010101001100001000101001
%d %i %f %u %lf %%%%!
Why oh Why?
- There are tons of programming languages to choose from,
but C is commonly chosen for its efficiency and low-level control.
- Therefore, understanding the foundation is important for all
C programmers.
- This will help you understand “why” for a lot of things,
and help prevent inadvertent errors.
I’m thinking of a number…
- First we need a number for examples.
- How about 0644 in octal.
- It’s the number you use in
chmod
from CS155 to make a file be
readable and writeable by you and readable by everyone else, so why not?
- The 0 in the beginning just means it is in octal.
- What is it though?
- In C it is an
int
.
- Everything is an
int
or a double
unless you say otherwise!
0644 in octal… okayyyyyy…
- What is it in different bases?
- So, first what does 644 mean in octal?
- 6 in the 64’s place (64 = 8²)
- 4 in the 8’s place (8 = 8¹)
- 4 in the 1’s place (1 = 80)
What is 0644 in binary?
Hardest way is convert to decimal, then convert to binary.
- Hard way:
- 6×8² = 384
- 4×8¹ = 32
- 4×10 = 4
- Total = 420, or 256+128+32+4, or 1101001002
- Easy Way: Break it into pieces of 3 each
- Why 3? 8¹ = 23, that’s why!
- One digit in octal is three digits in binary!
- 110 100 100 Look! 1102 = 6 and 1002 = 4
What is 0644 in octal?
What is 0644 in decimal?
- Octal to decimal
- Not hard, we just need to do the math.
- Fortunately, we love math!
- 6 × 8² + 4 × 8¹ + 4 × 80
- 6 × 64 + 4 × 8 + 4 × 1
- 384 + 32 + 4
- 420
What is 0644 in hexadecimal?
- Hard way:
- Hexadecimal is base 16, so the places are 4096, 256, 16, 1
- (163, 16², 16¹, 160)
- How many of each?
- 1 × 256 + 10 × 16 + 4 × 1
- So, it’s 0x1a4
What is 0644 in hexadecimal?
- Easy way:
- We already know it is 110100100 in binary.
- 16¹ = 24
- Each hex digit is 4 binary digits.
- 1 1010 0100
- 1 is easy: 1
- 1010 is 8+2 = 10 = a
- 0100 is 4 … so 4
- 0x1a4
Why doesn’t the easy way work for other conversions?
It’s all about the ratios:
From | To | Ratio | Difficulty |
Hexadecimal | Binary | 16 = 24 | Easy |
Hexadecimal | Octal | 16 = 84/3 | Not so bad |
Hexadecimal | Decimal | 16 = 101.20412 | Hard |
Decimal | Octal | 10 = 81.1073 | Hard |
Decimal | Binary | 10 = 23.3219 | Hard |
Octal | Binary | 8 = 23 | Easy |
Yow!
So, our number is:
110100100 in binary or… 644 in octal or… 420 in decimal or… 1a4 in hexadecimal…
- What’s the difference?!
- To a human? It’s the difference between a number and a headache.
- To the computer?
Nothing? Nothing at ALL!?
Nope, not a thing. Try it:
printf("%d\n", 0644);
printf("%d\n", 420);
printf("%d\n", 0x1a4);
420
420
420
Why??
The computer doesn’t care!
- It stores it all in binary.
- They all look the same in binary.
- They’re all just ints.
- In fact, even if it DID care, it can’t tell the difference anyway!
- If a memory cell contains, 000110110000101100101 was it binary, octal, decimal, or hex to begin with?
- So how does
printf
know how to print it?
How does it know?
- It doesn’t know—you have to tell it!
- For printing an
int
:
%d
means print out an int
as a signed decimal.
%u
means print out an int
as an unsigned decimal.
%x
means print out an int
as an unsigned hexadecimal
%o
means print out an int
as an unsigned octal
- There are more:
long
, unsigned long
, short
, unsigned short
, float
,
double
, long double
, long long
, unsigned long long
,
char
, unsigned char
, …, etc.
- We’ll cover more later… this is enough for now.
What if it is isn’t an int
?
%s
will print a string (array of char
)
%f
will print a float
%lf
will print a double
%lu
will print an unsigned long
%ld
(or %li
) will print a signed long
MAKE THEM MATCH!!!!
- It is vital that the format string matches the type of the value
you are printing.
- What happens if you tell it to print an
int
(%d
or %x
or %o
),
but give it a float?
Best case: Bad things happen.
printf("%d",3.14f);
c.c: In function 'main':
c.c:1: warning: format '%d' expects argument of type 'int', but argument 2 has type 'double'
-1610301720
- This is a lie! You are telling it that you are giving it
an
int
that you want to print in decimal,
but you are giving it a double
instead.
- It will believe the lie!
- Badness results.
Worst Case: Crash!
printf("%s", 3.14);
- HUGE LIE
printf
is expecting a string, which is an array of char
.
- You gave it a
double
—they are SO different that it might crash!
So, whats the “&” and scanf?
- In C, “&” is the “address-of” operator.
- Everything lives in memory.
- If you have an
int
named fred
, &fred
is the physical location
in memory where fred is stored. It is his address.
- Why does
scanf
need that?
scanf reads values IN
- It needs to know where to store them.
- It reads in what you tell it
- converts it from whatever base you tell it
- converts it to whatever type you tell it
- and puts the result where you tell it
- Where = address
- Why doesn’t
printf
need the address too?
Lying to scanf
- What happens if you lie to scanf?
- It will do as it is told.
- If you lie, you get what you deserve
- probably a big fat core dump.
int foo;
scanf("%f",&foo);
What is in foo
?
Why not & in printf?
- printf doesn’t care where they are stored; it only needs the values to print.
- It won’t modify any memory, so you can just give it the value.
scanf
needs to modify memory, so you need to tell it what memory
to modify, and what type of thing to store there.
Example
#include <stdio.h>
int main() {
int fred = 12;
printf("%d", fred);
return 0;
}
- What gets passed into
printf
?
- If you said
fred
, you’re wrong!
12
gets passed. Not fred
. A copy of the contents of fred
.
printf
can’t touch fred
even if it wanted to.
fred
may as well be in GTMO! He’s untouchable.
Example2
#include <stdio.h>
int main() {
int fred = 12;
scanf("%d", fred);
return 0;
}
- Why is this wrong?
- What can
scanf
do with a “12”?
scanf
doesn’t care what was in fred
.
- It wants to know where
fred
lives,
so that it can change the contents of it.
- Hence, it wants
&fred
, the address of fred
.
Example2
#include <stdio.h>
int main() {
int fred = 12;
scanf("%d", fred);
return 0;
}
- So what will happen if we do this anyway? What does it mean?
- “Read a number user and store it as a signed int into memory address 12”
- What’s in memory locatino 12?
- A BIG FAT SEGMENTATION FAULT
- The chances that memory location 12 is YOURS to touch is minuscule.
- The chances are good that it is part of the operating system.
- Mess with memory that isn’t yours, and you get a segfault
right between the eyes!
Example2
#include <stdio.h>
int main() {
int fred = 12;
scanf("%d", &fred);
return 0;
}
- This is what it should be.
- “Fred is over there”
What about strings?
int main() {
char name[6];
strcpy(name, "Bubba");
printf("%s\n", name);
return 0;
}
name[0] name[1] name[2] name[3] name[4] name[5]
┌────────┬────────┬────────┬────────┬────────┬────────┐
│ B │ u │ b │ b │ a │ \0 │
└────────┴────────┴────────┴────────┴────────┴────────┘
- name[1] is a
char
(in fact, it is a ‘u’)
- But what is
name
?
- It might sound odd, but
name
is the address where the chars start.
name == &name == &name[0]
What about strings?
int main() {
char name[6];
strcpy(name, "Bubba");
printf("%s\n", name);
return 0;
}
name[0] name[1] name[2] name[3] name[4] name[5]
┌────────┬────────┬────────┬────────┬────────┬────────┐
│ B │ u │ b │ b │ a │ \0 │
└────────┴────────┴────────┴────────┴────────┴────────┘
- What’s passed to
printf
?
- A pointer to the beginning of the string.
- It will read the characters, and stop reading when it hits a
\0
.
- Why not pass in a copy of the contents of the array
(like what happens with ints?)
- Too much copying.
What about strings?
int main() {
char name[6];
strcpy(name, "Bubba");
scanf("%s\n", name);
return 0;
}
name[0] name[1] name[2] name[3] name[4] name[5]
┌────────┬────────┬────────┬────────┬────────┬────────┐
│ B │ u │ b │ b │ a │ \0 │
└────────┴────────┴────────┴────────┴────────┴────────┘
- The same thing gets passed to
scanf
.
- Except that it will write there instead of read.
- And it will put a
\0
at the end.
It all sounds so delicate
- How can I prevent accidental mistakes, segfaults, cores, garbage data,
and other nasty things that go crash in the night?
- Easy. When you compile your program, add
-Wall
(big “W”, little “all”)
- It makes the compiler do some checking and tell you if a parameter doesn’t
match the format strings in
printf
, scanf
, etc.
- It won’t catch everything—that’s why they have you.
Let me say that again
c11 -Wall