#include struct Fraction { int num; int denom; }; void reduce_fraction(struct Fraction * f) { // keep minimal denom via least-common-multiple calculation (omitted) } void init_fraction(struct Fraction * f, int n, int d) { if(d==0) { fprintf(stderr,"aborting due to denominator of 0"); exit(1); } f->num = n; f->denom = d; reduce_fraction(f); } void play1() { struct Fraction f1; struct Fraction f2; init_fraction(&f1,1,3); init_fraction(&f2,7,14); // use f1 and f2 before they are deallocated implicitly // (also accessing them is very fast because they are on the stack) } // this is less idiomatic but makes perfect sense in modern C struct Fraction make_fraction(int n, int d) { struct Fraction ans; if(d==0) { fprintf(stderr,"aborting due to denominator of 0"); exit(1); } ans.num = n; ans.denom = d; return ans; } void play2() { struct Fraction f1 = make_fraction(1,3); struct Fraction f2 = make_fraction(7,14); // use f1 and f2 just as before } // but for arrays, this pointer version can share an initialization loop and // there is no way in C for the non-pointer version void init_fraction_array(int len, struct Fraction *f, int n, int d) { int i; for(i=0; i < len; ++i) init_fraction(&f[i],n,d); } void play3() { struct Fraction arr1[10]; struct Fraction arr2[35]; init_fraction_array(10,arr1,1,3); // this passes the array's address init_fraction_array(35,arr2,7,14); // this does too // we now have 45 stack-allocated fractions to play with // the other way: {struct Fraction arr3[7]; int i; for(i=0; i < 7; ++i) arr3[i] = make_fraction(1,3); } }