structs and Fractions in C |
struct student {
int student_num;
double gpa;
char sex;
};Here is a short program that makes use of this struct:
| Program to illustrate a struct | Output of Program |
|---|---|
1 #include <stdio.h> /* for input/output */
2 struct student { /* no storage allocated */
3 int num; /* student number */
4 double gpa; /* grade point average */
5 char sex; /* sex (M or F) */
6 };
7
8 void printstudent(struct student s); /* prototype */
9
10 int main() {
11 /* allocate storage and initialize it */
12 struct student joe = {243167, 3.4, 'M'};
13 struct student jim; /* just allocate storage */
14 struct student jeb; /* just allocate storage */
15 jim.num = 93745; /* initialize first field of jim */
16 jim.gpa = 2.9; /* initialize second field of jim */
17 jim.sex = 'M'; /* initialize third field of jim */
18
19 jeb = jim; /* copy jim's fields into jeb's storage */
20 printstudent(joe);
21 printstudent(jim);
22 printstudent(jeb);
23 }
24
25 /* print the fields of an input struct student */
26 void printstudent(struct student s) {
27 printf("Student Number: %i\n", s.num);
28 printf(" Grade Point Ave: %0.2f\n", s.gpa);
29 printf(" Sex (M or F): %c\n\n", s.num);
30 }
| Output: Student Number: 243167 Grade Point Ave: 3.40 Sex (M or F): ß Student Number: 93745 Grade Point Ave: 2.90 Sex (M or F): 1 Student Number: 93745 Grade Point Ave: 2.90 Sex (M or F): 1 |
The definition of the struct (its form or pattern) is given in green above. No storage is allocated by this code. A statement such as struct student suzy; actually allocates storage, which is not initialized. The program illustrates three ways to initialize the fields of a struct: with {} iniitalization (as with an array), with assignments for each field (which also works for an array), and with a single assignment (which does not work for an array.
When a struct is passed as a parameter, it is copied into the function (unlike an array, whose address is passed). When a struct is assigned or returned from a function, again it is copied. Normally in C, one uses pointers to structs, but we won't do that in this course because of the additional complexity.
What is presented here is only one of several ways to do structs in C. With this way, you always have to write struct student as if it were a type such as int or double. The keyword struct must always come before the name of the struct. Although the situation in C is more complicated that what we are describing here, what happens in C++ is much more complicated yet. The C struct is generalized to the C++ class, which is the foundation for C++.
struct frac { /* a fraction, numer/denom */
int numer; /* numerator of fraction */
int denom; /* denominator of fraction */
};Notice that this struct has two integers as its fields, so this example could in theory be handled using an array of size 2. However, we are trying to teach about structs, and there are some other advantages of using structs.
First look at a stripped-down version of the function init that takes two integers, and builds a struct frac out of them, which it returns, for other functions to use.
/* create fraction a/b, in lowest terms */
struct frac init(int a, int b) {
struct frac z; /* storage for new struct frac (uninitialized) */
z.numer = a; /* put a in the first field, as the numerator */
z.denom = b; /* put b in the first field, as the denominator */
return z; /* return a copy of this struct frac */
}Following this style, the function add, which adds two fractions and returns the sum, as a fraction, might look as follows:
/* add fractions x and y, return sum */
struct frac add(struct frac x, struct frac y) {
struct frac z; /* storage for new struct frac (uninitialized) */
z.numer = x.denom*y.numer + y.denom*x.numer; /* define the numerator */
z.denom = x.denom * y.denom; /* define the denominator */
return z; /* return a copy of the sum */
}However, it is more convenient to cleverly use the init function to help with the definition of add:
/* add fractions x and y, return sum */
struct frac add(struct frac x, struct frac y) {
return init(x.denom*y.numer + y.denom*x.numer, /* the numerator */
x.denom * y.denom); /* the denominator */
}Then we have a function reduce that reduces a function to lowest terms, using the gcd (greatest common divisor), and returns the reduced fraction. This function can be placed at the end of init, so it will be called by all the other functions that use init.
The example below gives the example of a function expfrac that will use the series we had before to calcuate approximate values for exp(x), for a fraction x.
| Program to Implement Fractions in C | Rest of the Program |
|---|---|
#include <stdio.h> /* for input/output */
struct frac { /* a fraction, numer/denom */
int numer; /* numerator of fraction */
int denom; /* denominator of fraction */
};
int gcd(int a, int b);
struct frac init(int a, int b);
struct frac add(struct frac x, struct frac y);
struct frac mul(struct frac x, struct frac y);
struct frac intmul(struct frac x, int r);
struct frac intdiv(struct frac x, int r);
struct frac sub(struct frac x, struct frac y);
struct frac inv(struct frac x);
struct frac divid(struct frac x, struct frac y);
double todouble(struct frac x);
int fraccomp(struct frac x, struct frac y);
struct frac expfrac(struct frac x, int n);
void writefrac(struct frac x);
struct frac readfrac();
int main() {
int a, b;
struct frac p = readfrac();
struct frac q = expfrac(p,12);
}
/* greatest common divisor */
int gcd(int a, int b) {
while (b != 0) {
int temp = b;
b = a%b;
a = temp;
}
return a;
}
/* reduce z to lowest terms */
struct frac reduce(struct frac x) {
struct frac z;
int c = gcd(x.numer, x.denom);
z.numer = x.numer/c;
z.denom = x.denom/c;
return z;
}
/* create fraction a/b, in lowest terms */
struct frac init(int a, int b) {
struct frac z;
if (b == 0) { /* can't have 0 denom */
printf("WARNING! Zero denom!\n");
b = 1;
}
if (b < 0) { /* put sign in the numerator */
a = -a;
b = -b;
}
z.numer = a;
z.denom = b;
z = reduce(z);
return z;
}
/* convert fraction x to a double */
double todouble(struct frac x) {
return (double)x.numer/(double)x.denom;
}
/* conpare fraction x to fraction y */
int fraccomp(struct frac x, struct frac y) {
struct frac z = sub(x, y);
if (z.numer == 0) return 0; /* x == y */
else if (z.numer < 0) return -1; /* x < y */
else return 1; /* x > y */
}
| /* add fractions x and y, answer in l. terms */
struct frac add(struct frac x, struct frac y) {
/* not doing lazy way here, lcm in denom */
int c = gcd(x.denom, y.denom);
return init(x.denom/c*y.numer +
y.denom/c*x.numer,
x.denom/c * y.denom);
}
/* multiply fractions x and y, answer in l.t. */
struct frac mul(struct frac x, struct frac y) {
return init(x.numer*y.numer, x.denom*y.denom);
}
/* multiply fraction x by int r, answer in l.t.*/
struct frac intmul(struct frac x, int r) {
return init(r*x.numer, x.denom);
}
/* divide fraction x by int r, answer in l.t. */
struct frac intdiv(struct frac x, int r) {
return init(x.numer, x.denom * r);
}
/* fraction x minus fraction y, answer in l.t. */
struct frac sub(struct frac x, struct frac y) {
return add(x, intmul(y, -1));
}
/* reciprocal of fraction x, answer in l.t. */
struct frac inv(struct frac x) {
return init(x.denom, x.numer);
}
/* divide fraction x by y, answer in l.t. */
struct frac divid(struct frac x, struct frac y) {
return mul(x, inv(y));
}
/* write the fraction, not necessarily in l.t. */
void writefrac(struct frac x) {
printf("%i/%i", x.numer, x.denom);
}
/* read two ints, create fraction, reduce */
struct frac readfrac() {
int a, b;
scanf("%i %i", &a, &b);
return init(a, b);
}
/* for fraction x, calc. exp(x) as fraction */
struct frac expfrac(struct frac x, int n) {
int i;
struct frac coeff = init(1, 1);
struct frac zpower = init(1, 1);
struct frac term = init(1, 1);
struct frac sum = init(1, 1);
for (i = 1; i <= n; i++) {
coeff = intdiv(coeff, i);
zpower = mul(zpower, x);
term = mul(coeff, zpower);
sum = add(sum, term);
printf("# %i\n coeff: ", i);
writefrac(coeff);
printf("\n term: ");
writefrac(term);
printf("\n sum: ");
writefrac(sum);
printf("\n double: %0.12f\n",
todouble(sum));
}
return sum;
} |
| First run with x == 1/1 | Second run with x == 1/2 | Third run with x == 2/3 | Fourth run with x == 3/4 |
|---|---|---|---|
1 1 # 1 coeff: 1/1 term: 1/1 sum: 2/1 double: 2.000000000000 # 2 coeff: 1/2 term: 1/2 sum: 5/2 double: 2.500000000000 # 3 coeff: 1/6 term: 1/6 sum: 8/3 double: 2.666666666667 # 4 coeff: 1/24 term: 1/24 sum: 65/24 double: 2.708333333333 # 5 coeff: 1/120 term: 1/120 sum: 163/60 double: 2.716666666667 # 6 coeff: 1/720 term: 1/720 sum: 1957/720 double: 2.718055555556 # 7 coeff: 1/5040 term: 1/5040 sum: 685/252 double: 2.718253968254 # 8 coeff: 1/40320 term: 1/40320 sum: 109601/40320 double: 2.718278769841 # 9 coeff: 1/362880 term: 1/362880 sum: 98641/36288 double: 2.718281525573 # 10 coeff: 1/3628800 term: 1/3628800 sum: 9864101/3628800 double: 2.718281801146 # 11 coeff: 1/39916800 term: 1/39916800 sum: 13563139/4989600 double: 2.718281826198 # 12 coeff: 1/479001600 term: 1/479001600 sum: 260412269/95800320 double:2.718281828286 exp(1) = 2.7182818284590 | 1 2 # 1 coeff: 1/1 term: 1/2 sum: 3/2 double: 1.500000000000 # 2 coeff: 1/2 term: 1/8 sum: 13/8 double: 1.625000000000 # 3 coeff: 1/6 term: 1/48 sum: 79/48 double: 1.645833333333 # 4 coeff: 1/24 term: 1/384 sum: 211/128 double: 1.648437500000 # 5 coeff: 1/120 term: 1/3840 sum: 6331/3840 double: 1.648697916667 # 6 coeff: 1/720 term: 1/46080 sum: 75973/46080 double: 1.648719618056 # 7 coeff: 1/5040 term: 1/645120 sum: 354541/215040 double: 1.648721168155 # 8 coeff: 1/40320 term: 1/10321920 sum: 17017969/10321920 double: 1.648721265036 # 9 coeff: 1/362880 term: 1/185794560 sum: 306323443/185794560 double:1.648721270418 exp(1/2)=1.6487212707001 | 2 3 # 1 coeff: 1/1 term: 2/3 sum: 5/3 double: 1.666666666667 # 2 coeff: 1/2 term: 2/9 sum: 17/9 double: 1.888888888889 # 3 coeff: 1/6 term: 4/81 sum: 157/81 double: 1.938271604938 # 4 coeff: 1/24 term: 2/243 sum: 473/243 double: 1.946502057613 # 5 coeff: 1/120 term: 4/3645 sum: 7099/3645 double: 1.947599451303 # 6 coeff: 1/720 term: 4/32805 sum: 12779/6561 double: 1.947721383935 # 7 coeff: 1/5040 term: 8/688905 sum: 1341803/688905 double: 1.947732996567 # 8 coeff: 1/40320 term: 2/2066715 sum: 4025411/2066715 double:1.947733964286 exp(2/3)=1.9477340410546 | 3 4 # 1 coeff: 1/1 term: 3/4 sum: 7/4 double: 1.750000000000 # 2 coeff: 1/2 term: 9/32 sum: 65/32 double: 2.031250000000 # 3 coeff: 1/6 term: 9/128 sum: 269/128 double: 2.101562500000 # 4 coeff: 1/24 term: 27/2048 sum: 4331/2048 double: 2.114746093750 # 5 coeff: 1/120 term: 81/40960 sum: 86701/40960 double: 2.116723632813 # 6 coeff: 1/720 term: 81/327680 sum: 693689/327680 double: 2.116970825195 # 7 coeff: 1/5040 term: 243/9175040 sum: 3884707/1835008 double:2.116997310093 exp(3/4)=2.1170000166126 |