%% START CODE: [[union.c]] %% ```c #include <stdio.h> // Some I'm thinking we should have them implement // the object kind first with just something simple like // integer or float. Then you can see the size of the object. // // Then we can add the "coordinate" struct (or just an array directly) // and then have them guess what the sizeof value should be for that. // // This should hopefully illustrate that they all share the same memory space. typedef enum object_kind { INTEGER, FLOAT, CHAR, COORDINATE, } object_kind; typedef struct object { object_kind kind; // Things that are important about unions: // You have different fields to access the different types. // // And you need some what to know which of the fields is // the right one to access, this is why we have the "kind" field // above // // Should we talk about how this is allocated? What does it look like // in the actual memory layout? // // Should make them do some tests with sizeof to make sure their // intuition about calculating this makes sense. It will help // build confidence on how to think about allocations as we go // a bit further into what is going on. // // (It also makes more sense then why sometimes you will see or HAVE to see // using pointers for the items in a union) union { int int_v; float float_v; char char_v; int coords[3]; } data; } object; void print_object(object obj) { // They could implement this as part of an exercise. // It would be good example of both switch case, enum and union types. switch (obj.kind) { case INTEGER: printf("INTEGER: %d\n", obj.data.int_v); break; case FLOAT: printf("FLOAT: %f\n", obj.data.float_v); break; case CHAR: printf("CHAR: %c\n", obj.data.char_v); break; case COORDINATE: printf("COORDINATE: (%d, %d, %d)\n", obj.data.coords[0], obj.data.coords[1], obj.data.coords[2]); // Should show how you can still do this... so that's bad! // Not really the data you think is gonna be there // (I think the intuition from javascript/python land would be that // that this should print out 0 or give an error) // // LUL ERRORS IN C OMEGALUL printf(" BAD INT : %d\n", obj.data.int_v); break; default: printf("Unknown object kind\n"); break; } } int main() { object obj = {.kind = INTEGER, .data = {.int_v = 42}}; print_object(obj); // We can also use "compound literals" // NOTE: you must include the (object) at the beginning for this to work print_object((object){.kind = INTEGER, .data = {.int_v = 42}}); print_object((object){.kind = FLOAT, .data = {.float_v = 3.14}}); print_object((object){.kind = CHAR, .data = {.char_v = 'a'}}); print_object((object){.kind = COORDINATE, .data = {.coords = {1, 2, 3}}}); printf("sizeof(object) = %lu\n", sizeof(object)); } ``` %% END CODE %% - Sometimes you want to be able to store different kinds of things within the same struct - or a `union` of multiple different elements. Simple example ```c typedef enum { INTEGER, DOUBLE, } kind_t; typedef struct { kind_t kind; union { int data_int; double data_double; } data; } object; object obj = {.kind = INTEGER, .data = { data_int = 5 }}; printf("obj's integer: %d", obj.data.data_int); // So then what happens if we do this? printf("obj's double: %f", obj.data.data_double); ``` This is the way that you can store different **types** of data within one type. C doesn't have a concept of something like `int | double` that you might see in something like Typescript There's a couple of reasons for this: - C *must* know the size of a data type - Things in C must always be one "type", even though you can cast between then freely and lie to the compiler whenever you like :) - Things are still just one type at a time. So how can we have something like `int` and `double` stored in the same union? - If they have to be one size.... how does that work? - Well C just picks the biggest size of the item in h ```c #include <stdio.h> typedef struct { union { int data_int; char data_buffer[100]; }; } something; typedef struct { union { int data_int; int data_buffer[100]; }; } something_int; int main() { // 100 printf("sizeof(something) = %lu\n", sizeof(something)); // 400 printf("sizeof(something_int) = %lu\n", sizeof(something_int)); return 0; } ```