/* Creating a structs on the stack, * and passing it to methods that expects a heap-pointer. */ #include #include #include #include #include #include #define DBG(...) 0 //printf(__VA_ARGS__) #define STREQ(a,b) (strcmp((a),(b))==0) #define STRNEQ(a,b) (!STREQ(a,b)) /* * Things to note: * The use of "&" to get a pointer, when we need * to pass an immediate val-on-stack to a function that expects a pointer. * Understand how it works. * * Things entirely-unimportant: * How I included 'fflush' with printf-debugging * */ enum CatColor { BLACK, GREY, ORANGE, CALICO, TORTOISESHELL }; const char * const CatColor_str[] = { [BLACK] = "BLACK" ,[GREY] = "GREY" ,[ORANGE] = "ORANGE" ,[CALICO]="CALICO" ,[TORTOISESHELL]="TORTOISESHELL" }; // h/t: https://stackoverflow.com/a/58500930/320830 // safety check: static_assert( (sizeof(CatColor_str)/sizeof(CatColor_str[0]) == (TORTOISESHELL+1)), "CatColor enum has different size than CatColor_str" ); typedef enum CatColor CatColor; struct Cat { char* name; CatColor color; float clawSharpness; // A number between 0 and 1. }; typedef struct Cat Cat; // How much scratching might sharpen your claws. // The fraction between current-sharpness and perfect-sharpness (1.0) to improve. // const float SHARPENING_FACTOR = 0.10f; // Update this Cat, after it scratches something; // it may be something that `sharpens` the claws (like a tree), // or something that slightly dulls them (like human flesh). // void scratch( Cat* thiss, bool sharpens ) { if (! sharpens) { printf("Yeeowch!\n"); // presumably using their claws on a person thiss->clawSharpness -= SHARPENING_FACTOR*thiss->clawSharpness; } else { // get (say) 25% closer to full sharpness thiss->clawSharpness += SHARPENING_FACTOR*(1.0f-thiss->clawSharpness); } } void testAll(); // forward declaration int tripleArg(int k); int tripleArg_byRef(int *k); void assignToArg(Cat* c); void assignToArg_byRef(Cat* *c); Cat* new_Cat( char* _name, CatColor _color, float _clawSharpness ); char* catToString( Cat *c ); void setColor( Cat* this, CatColor _color ); void tryMutatePrimitiveVars(); void tryMutateObjectVars(); int main( int argc, char** args ) { tryMutatePrimitiveVars(); tryMutateObjectVars(); //testAll(); } void tryMutatePrimitiveVars() { int n = 32; printf( "n before: %d\n", n ); tripleArg(n); printf( "n after: %d\n", n ); printf( "\nLet's try again, but pass-by-ref:\n"); tripleArg_byRef(&n); printf( "n after: %d\n", n ); printf( "\n" ); } int tripleArg( int k ) { printf( " tripleArg: k=%d\n", k ); k = 3 * k; printf( " tripleArg: k=%d\n", k ); return k; } int tripleArg_byRef( int *k ) { printf( " tripleArg: *k=%d\n", *k ); *k = 3 * (*k); printf( " tripleArg: *k=%d\n", *k ); return *k; } void tryMutateObjectVars() { Cat* meowser = new_Cat( "Mr Purrscratch", ORANGE, 0.8 ); printf("meowser holds %ld, which prints as %s.\n", (long)meowser, catToString(meowser) ); printf("Uh-oh, painting the cat black:\n"); setColor(meowser, BLACK); printf("meowser holds %ld, which prints as %s.\n", (long)meowser, catToString(meowser) ); //printf( "Other methods CANNOT change my local var `meowser`, but CAN change the memory that meowser points to.\n" ); printf("\nNow calling assignToArg:\n"); assignToArg(meowser); printf("meowser holds %ld, which prints as %s.\n", (long)meowser, catToString(meowser) ); printf( "\nLet's try again, but pass-by-ref:\n"); assignToArg_byRef(&meowser); printf("meowser holds %ld, which prints as %s.\n", (long)meowser, catToString(meowser) ); } void assignToArg( Cat* some_kat ) { printf(" assignToArg: some_kat=%ld, which prints as %s.\n", (long)some_kat, catToString(some_kat) ); some_kat = new_Cat( "Kat", TORTOISESHELL, 0.4 ); printf(" assignToArg: some_kat=%ld, which prints as %s.\n", (long)some_kat, catToString(some_kat) ); } void assignToArg_byRef( Cat* *some_kat ) { printf(" assignToArg: *some_kat=%ld, which prints as %s.\n", (long)(*some_kat), catToString(*some_kat) ); (*some_kat) = new_Cat( "Kat", TORTOISESHELL, 0.4 ); printf(" assignToArg: *some_kat=%ld, which prints as %s.\n", (long)(*some_kat), catToString(*some_kat) ); } void setColor( Cat* thiss, CatColor _color ) { (*thiss).color = _color; } // @Override char* catToString( Cat *c ) { const int sz = strlen("Cat[") + strlen("name='") + strlen((*c).name) + strlen("', ") + strlen("color=") + strlen(CatColor_str[(*c).color]) + strlen(", ") + strlen("clawSharpness=") + 4 + strlen("]") +1; char* buffer = malloc(sz * sizeof(char)); // THINK of this as `char[] buffer = …` snprintf(buffer, sz, "Cat[name='%s', color=%s, clawSharpness=%4.2f]", c->name, CatColor_str[c->color], c->clawSharpness); DBG("sz is %d; buffer is: %s.\n", sz, buffer); return buffer; } // Canonical constructor Cat* new_Cat( char* _name, CatColor _color, float _clawSharpness ) { Cat* kitten = malloc(sizeof(Cat)); kitten->name = _name; kitten->color = _color; kitten->clawSharpness = _clawSharpness; return kitten; } // constructor w/ default clawSharpness Cat* new_Cat2( char* _name, CatColor _color ) { return new_Cat(_name, _color, 0.50f); } // This DOES work; upon return it copies back all (local) fields in its stack. Cat new_Cat_immediate( char* _name, CatColor _color, float _clawSharpness ) { Cat kitten; kitten.name = _name; kitten.color = _color; kitten.clawSharpness = _clawSharpness; return kitten; } // DANGER: // WHY WON'T THIS WORK: create a local Cat, and then return a pointer(Cat*) to it? // Cat* new_Cat_BAD( char* _name, CatColor _color, float _clawSharpness ) { Cat kitten; kitten.name = _name; kitten.color = _color; kitten.clawSharpness = _clawSharpness; return &kitten; } const float TOLERANCE = 0.000001f; bool equalCats( Cat* thiss, Cat* that ) { if (that == NULL) return false; else if (thiss == that) return true; // hot path -- not for correctness else return STREQ(thiss->name, that->name) && (thiss->color == that->color) && fabs(thiss->clawSharpness - that->clawSharpness) < TOLERANCE; } static void testConstructor() { printf("Testing constructor…"); // In practice, we might bundle this with tests of toString and equals. // (And if we used `record` instead of `class`, we would't need to!) // making sure our 'equals' works: assert( equalCats( new_Cat( "bartok", BLACK, 0.9f ), new_Cat( "bartok", BLACK, 0.9f ) )); assert(! equalCats( new_Cat( "bartok", BLACK, 0.9f ), new_Cat( "brahms", ORANGE, 0.9f ) )); // check that default constructor uses 0.50f sharpness: assert( equalCats( new_Cat2( "bartok", BLACK), new_Cat( "bartok", BLACK, 0.5f ) )); printf("done\n"); } static void testCatToString() { printf("Testing CatToString…"); assert( STREQ( catToString( new_Cat( "pepsi", CALICO, 0.50f)), "Cat[name='pepsi', color=CALICO, clawSharpness=0.50]" )); assert( STREQ( catToString( new_Cat( "pepsi", CALICO, 0.1234f)), "Cat[name='pepsi', color=CALICO, clawSharpness=0.12]" )); assert( STREQ( catToString( new_Cat2( "bartok", BLACK) ), "Cat[name='bartok', color=BLACK, clawSharpness=0.50]" )); printf("almost done…"); Cat pepsi; pepsi.name = "pepsi"; pepsi.color = CALICO; pepsi.clawSharpness = 0.50f; assert( STREQ( catToString( &pepsi ), "Cat[name='pepsi', color=CALICO, clawSharpness=0.50]" )); printf("done\n"); } static void testScratch() { printf("Testing scratch…"); fflush(stdout); /* Cat* bartok = new_Cat( "Bartok", BLACK, 0.80f ); Cat* garfield = new_Cat( "Garfield", ORANGE, 0.50f ); */ Cat bartok; bartok.name = "Bartok"; bartok.color = BLACK; bartok.clawSharpness = 0.80f; Cat garfield; garfield.name = "Garfield"; garfield.color = ORANGE; garfield.clawSharpness = 0.50f; // COPYPASTA error, the first time I wrote the above. // I ran and got "Segmentation Fault" (in the next line). // Can you guess what I'd done wrong, above? // NOTE tests are tuned for SHARPENING_FACTOR = 0.10; // must re-write some if that changes. //printf("garfield is %s.\n", catToString(&garfield)); //fflush(stdout); scratch(&garfield, false); //printf("garfield is %s.\n", catToString(&garfield)); //fflush(stdout); assert( equalCats(&garfield, new_Cat("Garfield", ORANGE, 0.45f) ) ); scratch(&garfield, false); assert( equalCats(&garfield, new_Cat("Garfield", ORANGE, 0.45f-0.045f ) ) ); assert( equalCats(&bartok, new_Cat("Bartok", BLACK, 0.80f) ) ); // With mutation, testing *includes* checking that we didn't // modify other fields, NOR other fields of other objects // (e.g. that we didn't accidentally declare a field `static`). scratch(&bartok,true); assert( equalCats(&bartok, new_Cat("Bartok", BLACK, 0.82f) ) ); printf("done\n"); } void testAll() { testConstructor(); testCatToString(); testScratch(); } /** * @author ibarland * @version 2025-???-?? * * @license CC-BY -- share/adapt this file freely, but include attribution, thx. * https://creativecommons.org/licenses/by/4.0/ * https://creativecommons.org/licenses/by/4.0/legalcode * Including a link to the *original* file satisifies "appropriate attribution". */