Chapter 6: Structures.
Original C examples from the existing C programming archive. Source text is preserved; these historical examples have not been compiled or tested against a current toolchain.
struct1.c
This example groups x/y coordinates into a point and two points into a rectangle. It assigns members and uses the distance formula to compute a point distance from the origin. The sqrt call needs the appropriate math-library linkage on relevant toolchains; the old comment is preserved rather than treated as a verified build command.
/* gcc -o test test.c -l */
#include<stdio.h>
#include<math.h>
struct point
{
int x;
int y;
};
struct rect
{
struct point pt1;
struct point pt2;
};
struct point pt;
struct rect screen = { {0,0,},{8,16} };
int main()
{
double dist;
pt.x=5, pt.y=10;
printf("%d,%d\n", pt.x, pt.y);
dist = sqrt((double)pt.x*pt.x + (double)pt.y*pt.y);
printf("Distance from origin: %.2f\n",dist);
printf("%d,%d\n",screen.pt1.x, screen.pt2.x);
printf("%d,%d\n",screen.pt1.y, screen.pt2.y);
return 0;
}
keycount.c
This keyword counter tokenizes input, searches a sorted table of word/count structures with binary search and increments the matching entry. The array stores both the vocabulary and its frequencies. Its simple tokenizer is not a complete C lexer, so strings, comments and other language constructs can affect what it counts.
#include<stdio.h>
#include<ctype.h>
#include<string.h>
struct key
{
char *word;
int count;
} keytab[] = {
"auto", 0,
"break", 0,
"case", 0,
"char", 0,
"const", 0,
"continue", 0,
"default", 0,
"do", 0,
"double", 0,
"else", 0,
"enum", 0,
"extern", 0,
"float", 0,
"for", 0,
"goto", 0,
"if", 0,
"int", 0,
"long", 0,
"register", 0,
"return", 0,
"short", 0,
"signed", 0,
"sizeof", 0,
"static", 0,
"struct", 0,
"switch", 0,
"typedef", 0,
"union", 0,
"unsigned", 0,
"void", 0,
"volatile", 0,
"while", 0
};
#define BUFSIZE 100
#define MAXWORD 100
#define NKEYS (sizeof keytab / sizeof(keytab[0]))
int getword(char *, int);
int binsearch1(char *, struct key *, int);
char buf[BUFSIZE];
int bufp=0;
int main()
{
int n;
char word[MAXWORD];
while (getword(word, MAXWORD) != EOF)
if (isalpha(word[0]))
if ((n = binsearch1(word, keytab, NKEYS)) >= 0)
keytab[n].count++;
for(n=0; n < NKEYS; n++)
if (keytab[n].count > 0)
printf("%4d %s\n", keytab[n].count, keytab[n].word);
return 0;
}
int binsearch1(char *word, struct key tab[], int n)
{
int cond;
int low, high, mid;
low = 0;
high = n-1;
while (low <= high)
{
mid = (low+high)/2;
if ((cond = strcmp(word, tab[mid].word)) < 0)
high = mid-1;
else if (cond > 0)
low = mid+1;
else
return mid;
}
return -1;
}
int getword(char *word, int lim)
{
int c, getch(void);
void ungetch(int);
char *w = word;
while (isspace(c=getch()));
if(c!=EOF) *w++=c;
if(!isalpha(c))
{ *w = '\0';
return c;
}
for( ; --lim > 0; w++)
if (!isalnum(*w = getch()))
{
ungetch(*w);
break;
}
*w = '\0';
return word[0];
}
int getch(void)
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c)
{
if (bufp >= BUFSIZE) printf("ungetch: too many characters\n");
else
buf[bufp++]=c;
}
keycountptr.c
This keyword-counter variation returns a pointer to the matching table entry rather than an array index. The caller uses the arrow operator to update the count and then walks the table with pointers. It illustrates equivalent data access styles; pointer boundary arithmetic in a binary search requires special care when no match exists.
#include<stdio.h>
#include<ctype.h>
#include<string.h>
struct key
{
char *word;
int count;
} keytab[] = {
"auto", 0,
"break", 0,
"case", 0,
"char", 0,
"const", 0,
"continue", 0,
"default", 0,
"do", 0,
"double", 0,
"else", 0,
"enum", 0,
"extern", 0,
"float", 0,
"for", 0,
"goto", 0,
"if", 0,
"int", 0,
"long", 0,
"register", 0,
"return", 0,
"short", 0,
"signed", 0,
"sizeof", 0,
"static", 0,
"struct", 0,
"switch", 0,
"typedef", 0,
"union", 0,
"unsigned", 0,
"void", 0,
"volatile", 0,
"while", 0
};
#define BUFSIZE 100
#define MAXWORD 100
#define NKEYS (sizeof keytab / sizeof(keytab[0]))
int getword(char *, int);
struct key *binsearch1(char *, struct key *, int);
char buf[BUFSIZE];
int bufp=0;
int main()
{
char word[MAXWORD];
struct key *p;
while (getword(word, MAXWORD) != EOF)
if (isalpha(word[0]))
if ((p = binsearch1(word, keytab, NKEYS)) != NULL)
p->count++;
for(p=keytab; p < keytab + NKEYS; p++)
if (p->count > 0)
printf("%4d %s\n", p->count, p->word);
return 0;
}
struct key *binsearch1(char *word, struct key *tab, int n)
{
int cond;
struct key *low = &tab[0];
struct key *high = &tab[n];
struct key *mid;
while (low < high)
{
mid = (low + (high-low)/2);
if ((cond = strcmp(word, mid->word)) < 0)
high = mid;
else if (cond > 0)
low = mid+1;
else
return mid;
}
return NULL;
}
int getword(char *word, int lim)
{
int c, getch(void);
void ungetch(int);
char *w = word;
while (isspace(c=getch()));
if(c!=EOF) *w++=c;
if(!isalpha(c))
{ *w = '\0';
return c;
}
for( ; --lim > 0; w++)
if (!isalnum(*w = getch()))
{
ungetch(*w);
break;
}
*w = '\0';
return word[0];
}
int getch(void)
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c)
{
if (bufp >= BUFSIZE) printf("ungetch: too many characters\n");
else
buf[bufp++]=c;
}
middle.c
This geometry example returns a complete point structure from makepoint and combines rectangle corner coordinates to obtain a midpoint. Integer division means a non-integral midpoint would be truncated. It demonstrates nested aggregate data and structure return values rather than dynamic allocation.
#include<stdio.h>
#define XMAX 10
#define YMAX 20
struct point
{
int x;
int y;
};
struct rect
{
struct point pt1;
struct point pt2;
};
struct point middle;
struct rect screen;
struct point makepoint(int x, int y);
int main()
{
screen.pt1 = makepoint(0,0);
screen.pt2 = makepoint(XMAX, YMAX);
middle = makepoint ((screen.pt1.x + screen.pt2.x)/2 , (screen.pt1.y + screen.pt2.y)/2);
printf("Middle = %d, %d\n",middle.x, middle.y);
return 0;
}
struct point makepoint(int x, int y)
{
struct point temp;
temp.x = x;
temp.y = y;
return temp;
}
ptinrect.c
This extension adds coordinate-wise point addition and a rectangle containment predicate. The predicate includes lower bounds and excludes upper bounds, a useful half-open convention. The preserved printf passes an entire returned structure to integer format placeholders instead of printing its individual fields, so that output call is invalid.
#include<stdio.h>
#define XMAX 10
#define YMAX 20
struct point
{
int x;
int y;
};
struct rect
{
struct point pt1;
struct point pt2;
};
struct point middle;
struct rect screen;
struct point makepoint(int x, int y);
struct point addpoint (struct point p1, struct point p2);
int ptinrect(struct point p, struct rect r);
int main()
{
screen.pt1 = makepoint(0,0);
screen.pt2 = makepoint(XMAX, YMAX);
middle = makepoint ((screen.pt1.x + screen.pt2.x)/2 , (screen.pt1.y + screen.pt2.y)/2);
printf("Middle = %d, %d\n",middle.x, middle.y);
struct point pt3 = makepoint(XMAX, YMAX);
printf("Sum = %d , %d\n", addpoint(middle, pt3));
struct rect r = {{0,0},{30,30}};
printf("Point in rectangle: %s\n", ptinrect(pt3,r) ? "TRUE" : "FALSE");
return 0;
}
struct point makepoint(int x, int y)
{
struct point temp;
temp.x = x;
temp.y = y;
return temp;
}
struct point addpoint(struct point p1, struct point p2)
{
p1.x += p2.x;
p1.y += p2.y;
return p1;
}
int ptinrect(struct point p, struct rect r)
{
return (p.x >= r.pt1.x && p.x < r.pt2.x && p.y >= r.pt1.y && p.y < r.pt2.y);
}
ptstruc.c
This structure-pointer example accesses point members through both explicit dereferencing and the arrow operator, then reaches nested rectangle members. It shows that pp->x and (*pp).x name the same member. The rectangle and pointer refer to existing stack objects; no separate allocation is performed.
#include<stdio.h>
struct point
{
int x;
int y;
};
struct rect
{
struct point pt1;
struct point pt2;
};
int main()
{
struct point pt1 = {-10, -10};
struct point *pp;
pp = &pt1;
struct rect r = { {2,3},{5,6}}, *rp = &r;
printf("%d, %d \n", (*pp).x, (*pp).y);
printf("X = %d, %d and Y = %d, %d\n", rp->pt1.x, r.pt2.x, rp->pt1.y, rp->pt2.y);
return 0;
}
selfref.c
This word-frequency exercise builds a binary search tree whose nodes contain pointers to left and right children of the same structure type. Recursive insertion counts duplicates, and in-order traversal prints words in lexical order. Allocation failures are not fully handled and the tree is not balanced, so memory exhaustion and deep recursion remain practical limits.
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<string.h>
#define MAXWORD 100
#define BUFSIZE 100
struct tnode
{
char *word;
int count;
struct tnode *left;
struct tnode *right;
};
struct tnode *addtree(struct tnode *, char *);
struct tnode *talloc(void);
char *strdup1(char *);
void treeprint(struct tnode *);
int getword(char *, int);
int getch(void);
void ungetch(int c);
char buf[BUFSIZE];
int bufp=0;
int main()
{
struct tnode *root;
char word[MAXWORD];
root = NULL;
while (getword(word, MAXWORD) != EOF)
if (isalpha(word[0]))
root = addtree(root, word);
treeprint(root);
return 0;
}
struct tnode *addtree(struct tnode *p, char *w)
{
int cond;
if (p==NULL)
{
p = talloc();
p->word = strdup1(w);
p->count = 1;
p->left = p->right = NULL;
}
else if ((cond = strcmp(w, p->word)) == 0)
p->count++;
else if (cond < 0)
p->left = addtree(p->left, w);
else
p->right = addtree(p->right, w);
return p;
}
void treeprint(struct tnode *p)
{
if (p != NULL)
{
treeprint(p->left);
printf("%4d %s\n", p->count, p->word);
treeprint(p->right);
}
}
struct tnode *talloc(void)
{
return (struct tnode *) malloc(sizeof(struct tnode));
}
char *strdup1(char *s)
{
char *p;
p = (char *) malloc(strlen(s)+1);
if (p != NULL)
strcpy(p,s);
return p;
}
int getword(char *word, int lim)
{
int c, getch(void);
void ungetch(int);
char *w = word;
while (isspace(c=getch()));
if(c!=EOF) *w++=c;
if(!isalpha(c))
{ *w = '\0';
return c;
}
for( ; --lim > 0; w++)
if (!isalnum(*w = getch()))
{
ungetch(*w);
break;
}
*w = '\0';
return word[0];
}
int getch(void)
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c)
{
if (bufp >= BUFSIZE) printf("ungetch: too many characters\n");
else
buf[bufp++]=c;
}
selfref2.c
This archive variant keeps the same recursive word-frequency tree but raises both buffer limits to ten million. Larger constants do not make the algorithm scalable: the local word array can exceed ordinary stack limits, and an unbalanced tree still has poor worst-case depth. It is a comparison specimen, not an endorsed tuning change.
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<string.h>
#define MAXWORD 10000000
#define BUFSIZE 10000000
struct tnode
{
char *word;
int count;
struct tnode *left;
struct tnode *right;
};
struct tnode *addtree(struct tnode *, char *);
struct tnode *talloc(void);
char *strdup1(char *);
void treeprint(struct tnode *);
int getword(char *, int);
int getch(void);
void ungetch(int c);
char buf[BUFSIZE];
int bufp=0;
int main()
{
struct tnode *root;
char word[MAXWORD];
root = NULL;
while (getword(word, MAXWORD) != EOF)
if (isalpha(word[0]))
root = addtree(root, word);
treeprint(root);
return 0;
}
struct tnode *addtree(struct tnode *p, char *w)
{
int cond;
if (p==NULL)
{
p = talloc();
p->word = strdup1(w);
p->count = 1;
p->left = p->right = NULL;
}
else if ((cond = strcmp(w, p->word)) == 0)
p->count++;
else if (cond < 0)
p->left = addtree(p->left, w);
else
p->right = addtree(p->right, w);
return p;
}
void treeprint(struct tnode *p)
{
if (p != NULL)
{
treeprint(p->left);
printf("%4d %s\n", p->count, p->word);
treeprint(p->right);
}
}
struct tnode *talloc(void)
{
return (struct tnode *) malloc(sizeof(struct tnode));
}
char *strdup1(char *s)
{
char *p;
p = (char *) malloc(strlen(s)+1);
if (p != NULL)
strcpy(p,s);
return p;
}
int getword(char *word, int lim)
{
int c, getch(void);
void ungetch(int);
char *w = word;
while (isspace(c=getch()));
if(c!=EOF) *w++=c;
if(!isalpha(c))
{ *w = '\0';
return c;
}
for( ; --lim > 0; w++)
if (!isalnum(*w = getch()))
{
ungetch(*w);
break;
}
*w = '\0';
return word[0];
}
int getch(void)
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c)
{
if (bufp >= BUFSIZE) printf("ungetch: too many characters\n");
else
buf[bufp++]=c;
}
canon.c
This rectangle normalization routine uses coordinate-wise minima and maxima to construct ordered corners even when the supplied corners are reversed. Returning a new structure leaves the caller original unchanged. The example also demonstrates expression macros, which should receive arguments without side effects because a macro can evaluate an argument repeatedly.
#include<stdio.h>
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
struct point
{
int x;
int y;
};
struct rect
{
struct point pt1;
struct point pt2;
};
struct rect canonrect(struct rect r);
int main()
{
struct rect screen = { {9,7},{2,3} };
struct rect ans = canonrect(screen);
printf("X = %d, %d Y = %d, %d \n", ans.pt1.x, ans.pt2.x, ans.pt1.y, ans.pt2.y);
return 0;
}
struct rect canonrect(struct rect r)
{
struct rect temp;
temp.pt1.x = min(r.pt1.x, r.pt2.x);
temp.pt1.y = min(r.pt1.y, r.pt2.y);
temp.pt2.x = max(r.pt1.x, r.pt2.x);
temp.pt2.y = max(r.pt1.y, r.pt2.y);
return temp;
}