typedef and enum
For the last two lessons, every use of a struct meant writing struct student, which gets wordy. typedef gives a type a new name, so there's less to write:
1#include <stdio.h>2 3typedef struct {4 char name[20];5 int score;6} Student;7 8int main(void) {9 Student s = { "Ana", 95 };10 Student t = { "Ben", 88 };11 printf("%s: %d\n", s.name, s.score);12 printf("%s: %d\n", t.name, t.score);13 return 0;14}
Ana: 95 Ben: 88
Student is the name of this struct, with no struct in front. This lesson covers typedef, and something often used along with it: enum.
typedef makes an alias
You write it as typedef old_type new_name;. It doesn't create a new type; it only gives the existing type one more name, and the two names work exactly the same:
1#include <stdio.h>2 3typedef int Minutes;4 5Minutes total(Minutes a, Minutes b) {6 return a + b;7}8 9int main(void) {10 Minutes run = 25;11 Minutes walk = 40;12 printf("%d minutes in all\n", total(run, walk));13 return 0;14}
65 minutes in all
Minutes is int, and you still print it with %d. The benefit is that a reader can see at a glance what the number is: minutes, not just any integer. You've already used a name like this: size_t, the type strlen returns, is a name the standard library made with typedef. Behind it is an integer type with no negative numbers.
typedef struct
The most common use is naming a struct. The first example defined the struct and named it in one go. Split into two steps, it looks like this, and means the same:
1#include <stdio.h>2 3struct point {4 int x;5 int y;6};7 8typedef struct point Point;9 10int main(void) {11 Point a = { 3, 4 };12 struct point b = { 1, 2 };13 printf("(%d, %d) and (%d, %d)\n", a.x, a.y, b.x, b.y);14 return 0;15}
(3, 4) and (1, 2)
Point and struct point are the same type, and you can mix the two. That's why you'll often see it written all at once, giving you both names:
1typedef struct point {2 int x;3 int y;4} Point;
The point before the curly brace can be left out (the first example did). When is it required? When the struct needs to refer to itself, like a node in a linked list that points to the next node. That's the next lesson.
enum: integers with names
An order can only be in a few states: pending, paid, shipped, delivered. You could use 0, 1, 2 and 3, but a few days later you'll have forgotten what status == 2 means. An enumeration (enum) gives each of these numbers a name:
1#include <stdio.h>2 3enum status { PENDING, PAID, SHIPPED, DELIVERED };4 5int main(void) {6 enum status s = SHIPPED;7 printf("PENDING = %d\n", PENDING);8 printf("DELIVERED = %d\n", DELIVERED);9 printf("s = %d\n", s);10 if (s == SHIPPED) {11 printf("It has shipped\n");12 }13 return 0;14}
PENDING = 0 DELIVERED = 3 s = 2 It has shipped
Without numbers, the first name is 0 and each one after it is 1 more. The names really are int constants: you can compare them and print them with %d. By habit they're written in all capitals, so you can tell they're constants.
Choosing the numbers
You can also give a name a number. A name with a number uses it; the ones after it without a number keep counting up by 1. For example, to make Monday 1:
1#include <stdio.h>2 3enum weekday { MON = 1, TUE, WED, THU, FRI, SAT, SUN };4 5int main(void) {6 enum weekday today = WED;7 printf("Today is day %d of the week\n", today);8 printf("%d days until Sunday\n", SUN - today);9 return 0;10}
Today is day 3 of the week 4 days until Sunday
What does this program print?
1#include <stdio.h>2 3enum level { LOW = 1, MIDDLE, HIGH = 10, TOP };4 5int main(void) {6 printf("%d %d\n", MIDDLE, TOP);7 return 0;8}
LOW is set to 1, and MIDDLE has no number, so it counts up to 2. HIGH is set to 10, and TOP counts up from there to 11.
switch on an enum
enum and switch go together naturally: one case per state, and names are much clearer than numbers. The function below turns a state into words:
1#include <stdio.h>2 3typedef enum { PENDING, PAID, SHIPPED, DELIVERED } Status;4 5void show_status(Status s) {6 switch (s) {7 case PENDING:8 printf("Waiting for payment\n");9 break;10 case PAID:11 printf("Paid\n");12 break;13 case SHIPPED:14 printf("On its way\n");15 break;16 case DELIVERED:17 printf("Delivered\n");18 break;19 }20}21 22int main(void) {23 show_status(PAID);24 show_status(DELIVERED);25 return 0;26}
Paid Delivered
typedef enum { ... } Status; works like typedef struct: it gives the enum a short name, so from then on you just write Status. Don't forget the break after each case (lesson 5).
Putting them together
A struct can have an enum member. Here is a list of orders, each with a number and a state, and we count how many haven't arrived yet:
1#include <stdio.h>2 3typedef enum { PENDING, PAID, SHIPPED, DELIVERED } Status;4 5typedef struct {6 int id;7 Status status;8} Order;9 10int main(void) {11 Order orders[4] = { { 1001, DELIVERED }, { 1002, SHIPPED }, { 1003, PAID }, { 1004, DELIVERED } };12 int waiting = 0;13 for (int i = 0; i < 4; i++) {14 if (orders[i].status != DELIVERED) {15 printf("Order %d hasn't arrived\n", orders[i].id);16 waiting++;17 }18 }19 printf("%d still on the way\n", waiting);20 return 0;21}
Order 1002 hasn't arrived Order 1003 hasn't arrived 2 still on the way
Press Step: each Order is a box, and its status cell holds just an integer.
Tip: C doesn't check that an enum variable holds one of the listed numbers: Status s = 7; compiles fine. So it's a good idea to add a default to the switch, to handle numbers you didn't expect.
Practice
Write the code, then press Check. A hint costs nothing; the answer is there when you need it.
Rectangle
✓Use typedef struct to define Rect with the members width and height (both int). Write a function area that takes a Rect and returns its area. The program should print Area: 24.
Area: 24
1#include <stdio.h>2 3// define Rect and area here4 5int main(void) {6 Rect r = { 4, 6 };7 printf("Area: %d\n", area(r));8 return 0;9}
Cup sizes
✓Write a function price that takes a Size and uses switch to return the price: SMALL is 12, MEDIUM is 15, LARGE is 18. The program should print 12 15 18.
12 15 18
1#include <stdio.h>2 3typedef enum { SMALL, MEDIUM, LARGE } Size;4 5int price(Size s) {6 7}8 9int main(void) {10 printf("%d %d %d\n", price(SMALL), price(MEDIUM), price(LARGE));11 return 0;12}
Turn right
✓The directions go clockwise: north, east, south, west. Write a function turn_right that returns the direction after a right turn: north becomes east, and so on, and west goes back to north. The program starts facing north and turns 5 times, printing each new direction; the last line is East.
East South West North East
1#include <stdio.h>2 3typedef enum { NORTH, EAST, SOUTH, WEST } Direction;4 5Direction turn_right(Direction d) {6 7}8 9int main(void) {10 char names[4][8] = { "North", "East", "South", "West" };11 Direction d = NORTH;12 for (int i = 0; i < 5; i++) {13 d = turn_right(d);14 printf("%s\n", names[d]);15 }16 return 0;17}
What you learned
typedef old_type new_name;gives a type an alias without making a new type; that's wheresize_tcomes from.- After
typedef struct { ... } Student;you just writeStudent, with nostruct; a struct that refers to itself needs a name before the curly brace. enumnames a set of integers: by default they start at0and go up by 1; you can set your own, and the ones after count up from there.- Enum names are
intconstants: you can compare them and print them with%d, and they're written in capitals by habit. switchon anenumwith onecaseper value; C doesn't check what an enum holds, so add adefault.