class: center, middle # CMPE 30: Lecture 7 Numbers and Casting --- # Review: Why pick calloc over malloc + memset? 1. calloc is always faster 1. Clearer intent; the library can often skip the memset for fresh pages 1. malloc cannot allocate arrays 1. memset only works on chars 1. Ben got this wrong --- # Learning Objectives - To the CPU, everything is a number - Chars and pointers are numbers - Strings are arrays of numbers - `strtol` (and why not `atoi`) - Integer sizes and ranges - Floating-point surprises - Casting: `(type) value` - The `(int)"1999"` trap --- # Everything Is a Number .lc[ ```c char grade = 'A'; // same as: char grade = 65; printf("%c %d\n", grade, grade); // A 65 char next = grade + 1; printf("%c\n", next); // B ``` ] .rc[ - The CPU knows only numbers - Types tell the **compiler** how to use the bits - `'A'` **is** 65 --- no conversion - Char math is integer math ] --- # Pointers Are Numbers Too ```c int year = 1999; int *p = &year; printf("Address: %p\n", (void *)p); // e.g., 0x7ffc965e5104 ``` - A pointer is an integer used as a memory address - `int *` means "the number at this address is an `int`" - `%p` prints the number, usually in hex --- # Strings Are Not Special .lc[ ```c char word[] = "Hola"; printf("%s\n", word); for (int i = 0; i < (int)sizeof(word); i++) printf("%d ", word[i]); ``` Output: ``` Hola 72 111 108 97 0 ``` ] .rc[ - No native strings in C - Just an array of small integers - Ends with a `0` byte - `
` scans until the `0` --- that is the whole illusion ] --- # strtol ```c long strtol(const char *str, char **endptr, int base); long jenny = strtol("8675309", NULL, 10); // 8675309 --- Jenny, I got your number ``` - `str`: the text to parse - `endptr`: points past the digits (`NULL` if you don't care) - `base`: 10, 16, or 0 to auto-detect --- # strtol with Base 0 ```c strtol("42", NULL, 0); // 42 strtol("0x2A", NULL, 0); // 42 --- hex strtol("052", NULL, 0); // 42 --- octal! ``` **Trap:** a leading zero silently means octal. ```c strtol("010", NULL, 0); // 8, not 10 ``` Zero-padded decimal input? Pass base `10` explicitly. --- # atoi and Friends .lc[ ```c int n = atoi("banana"); // n == 0 // bad input, or a real zero? ``` ] .rc[ - Decimal only, no bases - No error reporting at all - Overflow = undefined behavior - Prefer `strtol` / `strtod` - `strtod` = `strtol` for `double` (no base argument) - Read `atoi`, don't write it ] --- # Numbers to Strings ```c char buf[16]; snprintf(buf, sizeof(buf), "%d", 1999); // buf is "1999" ``` - Formatting, in reverse - Full `sprintf` / `sscanf` story in chapter 10 --- # Integer Types | Type | Bytes | Range | |------|:-----:|-------| | `char` | 1 | -128 to 127 | | `short` | 2 | -32,768 to 32,767 | | `int` | 4 | about +/- 2.1 billion | | `long` | 8 | about +/- 9.2 quintillion | - Unsigned variants: `0` to `2^n - 1`; suffixes `U`, `UL`, `ULL` - Two's complement: one extra negative value - Explore with `sizeof` and `
` --- # The char Trap - Every other integer type is signed by default - Plain `char`? **Implementation-defined** - x86_64: signed. ARM: unsigned. - Spell out `signed char` / `unsigned char` when the range matters --- # The size_t Trap .lc[ ```c char *a = "Whip"; // 4 char *b = "Whip It!"; // 8 size_t d = strlen(a) - strlen(b); // 18446744073709551612 ``` ] .rc[ - `strlen` returns `size_t` --- unsigned - 4 - 8 wraps to a huge number - Cast **before** subtracting: ```c (long)strlen(a) - (long)strlen(b); // -4 ``` ] --- # Floating-Point Surprises .lc[ ```c float f = 1.2; if (f != 1.2) printf("what?!?\n"); if (f == 1.2f) printf("ok\n"); // BOTH print! ``` ] .rc[ - `1.2` is a `double` literal - Assigning to `f` rounds it - Promotion back to `double` cannot recover the lost bits - `1.2f` rounded the same way, so it matches - `0.1` and `0.2` are not exact in binary ] --- # Casting ```c double pi = 3.14159; int roughly_pi = (int)pi; // 3 --- truncates ``` - One syntax: `(type) value` --- C++ has four named casts, C has one - Float to int **truncates**, never rounds; too big to fit = UB - A cast says: "I know what I am doing, suppress the warnings" - No magic happens --- # What Casts Are Allowed? | From / To | Integer | Float | Pointer | | :--- | :--- | :--- | :--- | | **Integer** | yes | yes | yes | | **Float** | yes | yes | no | | **Pointer** | yes | no | yes | - Scalar types only - `char` counts as an integer type --- # The Classic Trap .lc[ ```c char *year = "1999"; int bad = (int)year; printf("%d\n", bad); // garbage address bits! ``` ``` warning: cast from pointer to integer of different size ``` ] .rc[ - Converts the **address**, not the text - 8-byte pointer chopped into a 4-byte `int` - The compiler even warns you - Parsing text is `strtol`'s job ] --- # void * and Byte Access .lc[ ```c int nums[] = {1984, 1985, 1986, 1987}; void *vp = nums; int *ip = (int *)vp; // ip[0] == 1984 char *bp = (char *)nums; // bp[0] == 0xc0 ``` ] .rc[ - `void *` points at anything - Why `malloc` needs no cast in C - `char *` = byte-by-byte view - `1984 = 0x7C0`; low byte first (little-endian) - Pointer-to-pointer casts: **you** must know the layout ] --- # Live Coding .lc[ ```c long jenny = strtol( "8675309", NULL, 10); double tempo = 118.9; // (int)tempo == 118 int hex_val = 0xbadd00d; unsigned char *raw = (unsigned char *)&hex_val; // 0d d0 ad 0b ``` ] .rc[ - `cc -Wall -Wextra -pedantic` - Dump the bytes of `"Xanadu"` - Cast `"1999"` to `(int)` and read the warning - Dump the bytes of `0xbadd00d` --- why backwards? (little-endian) ] --- # What does this print? ```c char c = 'A'; printf("%c %d\n", c + 1, c + 1); ``` 1. `B 66` 1. `A 65` 1. `B 65` 1. `66 B` 1. Ben got this wrong --- # Where is the bug? ```c char *track_str = "42"; int track = (int)track_str; printf("Track %d\n", track); ``` 1. `track_str` needs an `&` 1. `%d` should be `%s` 1. Cast converts the address, not the text 1. `"42"` is missing a null terminator 1. Ben got this wrong --- # What does this print? ```c printf("%ld\n", strtol("010", NULL, 0)); ``` 1. `10` 1. `8` 1. `2` 1. `0` 1. Ben got this wrong --- # Key Points - Everything is a number; types instruct the compiler - `'A'` is 65; char math is int math - Strings = arrays of small ints ending in `0` - `strtol` over `atoi` - `size_t` subtraction wraps - Compare floats with float literals - `(type) value` --- no magic - `(int)"1999"` gives the address, not 1999 **Read:** chapter 10 of *Gorgo C for C++ Programmers*. **Do:** exercises 1-7.