Skip to main content

Structures, Unions, and Enumerations

Exercises

Question 16.1

In the following declarations, the x and y structures have members named x and y:

struct { int x, y; } x;
struct { int x, y; } y;

Are these declarations legal on an individual basis? Could both declarations appear as shown in a program? Justify your answer.

Yes, the declarations are indeed legal. As seen in #8 of Chapter's Notes, we can declares structures as described in the question. One thing that seems like a minor inconvenience is that structure x and structure y are not considered as the same by the compiler, i.e. we can't assign the struct x to struct y and vice versa.


Question 16.2

  1. Declare structure variables named c1, c2, and c3, each having members real and imaginary of type double.
  2. Modify the declaration in part (a) so that c1's members initially have the values 0.0 and 1.0, while c2's members are 1.0 and 0.0 initially. (c3 is not initialized.)
  3. Write statements that copy the members of c2 into c1. Can this be done in one statement, or does it require two?
  4. Write statements that add the corresponding members of c1 and c2, storing the result in c3.
  1. We can declare (and define) a struct variable in one of three ways:

    1. Defining struct without any structure tag:
      struct {
      double real;
      double imaginary;
      } c1, c2, c3;
    2. Defining struct along with a structure tag:
      struct complex_number {
      double real;
      double imaginary;
      } c1, c2, c3;
    3. Defining a type of structure using typedef:
      typedef struct {
      double real;
      double imaginary;
      } complex_number;

    (a) is not really a good practice as if we later define another structure (without a tag) with similar members, this won't change the fact that the compiler will treat those two structures are different "types".

    (b) and (c) are preferred practices as it allows the programmer to create multiple variables of the same structure types.

  2. We can initialize the variables as follows:

    1.  
      struct {
      double real;
      double imaginary;
      } c1 = { 0.0, 1.0 },
      c2 = { .real = 1.0, .imaginary = 0.0 },
      c3;
    2.  
      struct complex_number {
      double real;
      double imaginary;
      } c1 = { .imaginary = 1.0, .real = 0.0 },
      c2 = { .real = 1.0, .imaginary = 0.0 },
      c3;
    3.  
      typedef struct {
      double real;
      double imaginary;
      } complex_number;

      complex_number c1 = { 0.0, 1.0 }, c2 = { .imaginary = 0.0, .real = 1.0 }, c3;
  3. We can only use a single statment to copy the members from one structure to another structure for the structures declared and defined above. There is a scenario when we need to use two statements to copy the members of a structure. Consider the program:

    struct {
    double real;
    double imaginary;
    } c1;

    struct {
    double real;
    double imaginary;
    } c2;

    c1 = { 1.0, 0.0 };
    c2.real = c1.real;
    c2.imaginary = c1.imaginary;

    In this case, we can't simply copy the members of structure c1 into the structure c2.

  4. The statement required is:

    c3.real = c1.real + c2.real;
    c3.imaginary = c1.imaginary + c2.imaginary;

Question 16.3

  1. Show how to declare a tag named complex for a structure with two members, real and imaginary, of type double.
  2. Use the complex tag to declare variables named c1, c2, and c3.
  3. Write a function named make_complex that stores its two arguments (both of type double) in a complex structure, then returns the structure.
  4. Write a function named add_complex that adds the corresponding members of its arguments (both complex structures), then returns the result (another complex structure).
#include <stdio.h>

/*
* complex: a structure that contains two members--real and imaginary--which acts
* as a data type that is capable to store a "complex" number
*/
struct complex {
double real;
double imaginary;

Question 16.4

Repeat Exercise 3, but this time using a type named Complex.

#include <stdio.h>

/*
* Complex: a structure that contains two members--real and imaginary--which acts
* as a data type that is capable to store a "complex" number
*/
typedef struct {
double real;
double imaginary;

Question 16.5

Write the following functions, assuming that the date structure contains three members: month, day, and year (all of type int).

  1. int day_of_year(struct date d);

    Returns the day of the year (an integer between 1 and 366) that corresponds to the date d.

  2. int compare_dates(struct date d1, struct date d2);

    Returns -1 if d1 is an earlier date than d2. +1 if d1 is a later date than d2, and 0 if d1 and d2 are the same.

#include <stdio.h>

/*
* date: A structure that has the members month (int), day (int), and year (int).
*/
struct date {
int month;
int day;
int year;

Question 16.6

Write the following function, assuming that the time structure contains three members: hours, minutes, and seconds (all of type int).

struct time split_time(long total_seconds);

total_seconds is a time represented as the number of seconds since midnight. The function returns a structure containing the equivalent time in hours (0-23), minutes (0-59), and seconds (0-59).

#include <stdio.h>

/*
* time: A structure that contains the members hours (int), minutes (int),
* and hours (int).
*/
struct time {
int hours;
int minutes;

Question 16.7

Assume that the fraction structure contains two members: numerator and denominator (both of type int). Write functions that perform the following operations on fractions:

  1. Reduce the fraction f to lowest terms. Hint: To reduce a fraction to lowest terms, first compute the greatest common divisor (GCD) of the numerator and denominator. Then divide both the numerator and denominator by the GCD.
  2. Add the fractions f1 and f2.
  3. Subtract the fraction f2 from the fraction f1.
  4. Multiply the fractions f1 and f2.
  5. Divide the fraction f1 by the fraction f2.

The fractions f, f1, and f2 will be arguments of type struct fraction: each function will return a value of type struct fraction. The fractions returned by the functions in parts (ii)-(v) should be reduced to lowest terms. Hint: You may use the function from part (i) to help write the functions in parts(ii)-(v).

#include <stdio.h>

struct fraction {
int numerator;
int denominator;
};

/*
* reduce_fraction: Takes the parameter "fraction_to_reduce" and uses the GCD

Question 16.8

Let color be the following structure:

struct color {
int red;
int green;
int blue;
};
  1. Write a declaration for a const variable named MAGENTA of type struct color whose members have the values 255, 0. and 255. respectively.
  2. (C99) Repeat part (i), but use a designated initializer that doesn't specify the value of green, allowing it to default to 0.
#include <stdio.h>

/*
* color: A structure that has the members red (int), green (int),
* and blue (int).
*/
struct color {
int red;
int green;

Question 16.9

Write the following functions. (The color structure is defined in Exercise 8.)

  1. struct color make_color(int red, int green, int blue);

    Returns a color structure containing the specified red, green, and blue values. If any argument is less than zero, the corresponding member of the structure will contain zero instead. If any argument is greater than 255. the corresponding member of the structure will contain 255.

  2. int getRed(struct color c);

    Returns the value of c's red member.

  3. bool equal_color(struct color color1, struct color color2);

    Returns true if the corresponding members of color1 and color2 are equal.

  4. struct color brighter(struct color c);

    Returns a color structure that represents a brighter version of the color c. The structure is identical to c, except that each member has been divided by 0.7 (with the result truncated to an integer). However, there are three special cases: (1) If all members of c are zero, the function returns a color whose members all have the value 3. (2) If any member of c is greater than 0 but less than 3, it is replaced by 3 before the division by 0.7. (3) If dividing by 0.7 causes a member to exceed 255, it is reduced to 255.

  5. struct color darker(struct color c);

    Returns a color structure that represents a darker version of the color c. The structure is identical to c, except that each member has been multiplied by 0.7 (with the result truncated to an integer).

#include <stdio.h>
#include <stdbool.h>

/*
* color: A structure with members red (int), green (int),
* and blue (int).
*/
struct color {
int red;

Question 16.10

The following structures are designed to store information about objects on a graphics screen:

struct point { int x, y; };
struct rectangle { struct point upper_left, lower_right; };

A point structure stores the x and y coordinates of a point on the screen. A rectangle structure stores the coordinates of the upper left and lower right corners of a rectangle. Write functions that perform the following operations on a rectangle structure r passed as an argument:

  1. Compute the area of r.
  2. Compute the center of r, returning it as a point value. If either the x or y coordinate of the center isn't an integer, store its truncated value in the point structure.
  3. Move r by x units in the x direction and y units in the y direction, returning the modified version of r. (x and y are additional arguments to the function.)
  4. Determine whether a point p lies within r, returning true or false. (p is an additional argument of type struct point.)
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdbool.h>

/*
* point: is a structure that has the members x (int), and y (int)
* as the coordinates in a 2D plane*/
struct point {

Question 16.11

Suppose that s is the following structure:

struct {
double a;
union {
char b[4];
double c;
int d;
} e;
char f[4];
} s;

If char values occupy one byte, int values occupy four bytes, and double values occupy eight bytes, how much space will a C compiler allocate for s? (Assume that the compiler leaves no "holes" between members.)

Assuming that the compiler leaves no holes, the struct contains the following members:

  1. A variable 'a' of type double.
  2. A union 'e' consisting of members: a. A character array of length 4. b. A variable of type double. c. A variable of type integer.
  3. An array of characters 'f' of length 4.

Based on this informations, we can say that the structure will have 8 bytes + max(4 bytes, 8 bytes, 4 bytes) + 4 * 1 bytes

So, the structure will occupy a space of 20 bytes.


Question 16.12

Suppose that u is the following union:

union {
double a;
struct {
char b[4];
double c;
int d;
} e;
char f[4];
} u;

If char values occupy one byte, int values occupy four bytes, and double values occupy eight bytes, how much space will a C compiler allocate for u? (Assume that the compiler leaves no "holes" between members.)

#include <stdio.h>

int main (void) {

union {
double a;
struct {
char b[4];
double c;

Question 16.13

Suppose that s is the following structure (point is a structure tag declared in Exercise 10):

struct shape {
int shape_kind; /* RECTANGLE or CIRCLE */
struct point center; /* coordinates of center */
union {
struct {
int height, width;
} rectangle;
struct {
int radius;
} circle;
} u;
} s;

If the value of shape_kind is RECTANGLE, the height and width members store the dimensions of a rectangle. If the value of shape_kind is CIRCLE, the radius member stores the radius of a circle. Indicate which of the following statements are legal, and show how to repair the ones that aren't:

  1. s.shape_kind = RECTANGLE;
  2. s.center.x = 10;
  3. s.height = 25;
  4. s.u.rectangle.width = 8;
  5. s.u.circle = 5;
  6. s.u.radius = 5;
  1. The statement isn't legal as the type of shape_kind is integer, not enum. To make the statement legal, we need to define shape_kind as:

    enum { RECTANGLE, CIRCLE } shape_kind;
  2. The statements are indeed legal as it accesses the member x of the structure center of tag point, of the structure s of tag shape.

  3. The statement isn't legal, as it is trying to access the member height of the structure s of tag shape. But height is not a member of the structure shape, but rather a member of the structure variable rectangle which is embedded inside the union variable u. To make the statement legal, we need to state it as:

    s.u.rectangle.height = 25;
  4. The statement is indeed legal.

  5. The statement is not legal as there is no specification on the member for the structure variable circle. The correct statement will be:

    s.u.circle.radius = 5;
  6. The statement is not legal as radius is not a member of the union variable u, but rather a member of the structure variable circle embedded inside the union variable u.


Question 16.14

Let shape be the structure tag declared in Exercise 13. Write functions that perform the following operations on a shape structure s passed as an argument:

  1. Compute the area of s.
  2. Move s by x units in the x direction and y units in the y direction, returning the modified version of s. (x and y are additional arguments to the function.)
  3. Scale s by a factor of c (a double value), returning the modified version of s. (c is an additional argument to the function.)
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

#define PI 3.141592

struct point {
int x, y;
};

Question 16.15

  1. Declare a tag for an enumeration whose values represent the seven days of the week.
  2. Use typedef to define a name for the enumeration of part (a).
#include <stdio.h>

int main (void) {

enum days { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY };

{
typedef enum { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY } days_t;
printf("Inside the block where a type definition of enumerator is declared as:\n" \

Question 16.16

Which of the following statements about enumeration constants are true?

  1. An enumeration constant may represent any integer specified by the programmer.
  2. Enumeration constants have exactly the same properties as constants created using #define.
  3. Enumeration constants have the values 0,1,2,... by default.
  4. All constants in an enumeration must have different values.
  5. Enumeration constants may be used as integers in expressions.
  1. True. We must declare enumeration constant and initialize it if necessary:

    enum { SPADES = 10, DIAMONDS = 20, HEARTS = 30, CLUBS = 40 } s1, s2;
  2. False. Enumeration constants must follow the C's block scope, i.e. enumeration defined inside a block won't be visible outside the block.

  3. True. Enumerations constants have 0, 1, 2, ... as the default value, but can be changed like:

    enum { VAR_1, VAR_2 = 10, VAR_3, VAR_4 = 20 } var;  /* VAR_1 = 0, VAR_2 = 10, VAR_3 = 11, VAR_4 = 20 */
  4. False. Enumeration constants can contain the same integer constant:

    enum { NUM_1 = 15, NUM_2 = 30, NUM_3 = 15, NUM_4 } num;   /* NUM_1 = 15, NUM_2 = 30, NUM_3 = 15, NUM_4 = 16 */
  5. True. We can use the enumeration constants defined in an expression:

    enum { SPADES = 10, DIAMONDS = 20, HEARTS = 30, CLUBS = 40 } s1, s2;
    int i;
    s1 = SPADES;
    s2 = CLUBS;
    i = s1 + s2;
    if (i == 50 && (s1 == CLUBS || s1 == SPADES) && (s2 == SPADES || s2 == CLUBS)) {
    printf("The selected card only consists of the color black\n");
    } else if (i == 50 && (s1 == DIAMONDS || s1 == HEARTS) && (s2 == DIAMONDS || s2 == HEARTS)) {
    printf("The selected card only consists of the color red\n");
    } else {
    printf("The selected cards consists of both color: black and red\n");
    }

Question 16.17

Suppose that b and i are declared as follows:

enum {FALSE, TRUE} b;
int i;

Which of the following statements are legal? Which ones are "safe" (always yield a meaningful result)?

  1. b = FALSE;
  2. b = i;
  3. b++;
  4. i = b;
  5. i = 2 * b + 1;
  1. Legal. The statement assigns the enumeration constant to the variable b - of type enum <anonymous>.
  2. Legal but not safe. i can be any integer, and if i contains an integer which none of the enumeration constant holds, we might have some problems. Like, We might be iterating the variable b over to check for a certain enumeration constant, but end up not getting any result.
  3. Legal but not safe. Enumeration might go out of bound.
  4. Legal. The statement assigns the integers that represents the enumeration constant stored in b.
  5. Legal. The statement results in 2 * (integer constant represented by the enumeration constant on b) + 1

Question 16.18

  1. Each square of a chessboard can hold one piece — a pawn, knight, bishop, rook, queen, or king — or it may be empty. Each piece is either black or white. Define two enumerated types: Piece, which has seven possible values (one of which is "empty"), and Color, which has two.
  2. Using the types from part (a), define a structure type named Square that can store both the type of a piece and its color.
  3. Using the Square type from part (b), declare an 8 x 8 array named board that can store the entire contents of a chessboard.
  4. Add an initializer to the declaration in part (c) so that board's initial value corresponds to the usual arrangement of pieces at the start of a chess game. A square that's not occupied by a piece should have an "empty" piece value and the color black.
#include <stdio.h>

int main (void) {

enum Piece { PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING, EMPTY };
enum Color { BLACK, WHITE };

struct Square {
enum Piece piece;

Question 16.19

Declare a structure with the following members whose tag is pinball_machine:

name - a string of up to 40 characters
year - an integer (representing the year of manufacture)
type - an enumeration with the values EM (electromechanical) and SS (solid state)
players - an integer (representing the maximum number of players)

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
// #include <string.h>

#define PM_NAME_LEN 40
#define MAX_PM_SPEC 100

/*

Question 16.20

Suppose that the direction variable is declared in the following way:

enum {NORTH, SOUTH, EAST, WEST} direction;

Let x and y be int variables. Write a switch statement that tests the value of direction, incrementing x if direction is EAST, decrementing x if direction is WEST, incrementing y if direction is SOUTH, and decrementing y if direction is NORTH.

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

int main (void) {

enum { NORTH, SOUTH, EAST, WEST } direction;
int x = 0, y = 0;

Question 16.21

What are the integer values of the enumeration constants in each of the following declarations?

  1. enum {NUL, SOH, STX, ETX};
  2. enum {VT = 11, FF, CR};
  3. enum {SO = 14, SI, DLE, CAN = 24, EM};
  4. enum {ENQ = 45, ACK, BEL, LF = 37, ETB, ESC};

The corresponding integer constant for the enumeration constants are:

  1. NUL: 0
    SOH: 1
    STX: 2
    ETX: 3
  2. VT: 11
    FF: 12
    CR: 13
  3. SO:   14
    ST: 15
    DLE: 16
    CAN: 24
    EM: 25
  4. ENO:  45 
    ACK: 46
    BEL: 47
    LF: 37
    ETB: 38
    ESC: 39

Question 16.22

Let chess_pieces be the following enumeration:

enum chess_pieces {KING, QUEEN, ROOK, BISHOP, KNIGHT, PAWN};

  1. Write a declaration (including an initializer) for a constant array of integers named piece_value that stores the numbers 200, 9, 5, 3, 3, and 1, representing the value of each chess piece, from king to pawn. (The king's value is actually infinite, since "capturing” the king (checkmate) ends the game, but some chess-playing software assigns the king a large value such as 200.)
  2. (C99) Repeat part (a), but use a designated initializer to initialize the array. Use the enumeration constants in chess_pieces as subscripts in the designators. (Hint: See the last question in Q&A for an example.)
#include <stdio.h>
#include <stdlib.h>

int main (void) {

enum chess_pieces { KING, QUEEN, ROOK, BISHOP, KNIGHT, PAWN };

const int chess_pieces[] = { 200, 9, 5, 3, 3, 1 };

Programming Projects

Project 16.1

Write a program that asks the user to enter an international dialing code and then looks it up in the country_codes array (see Section 16.3). If it finds the code, the program should display the name of the corresponding country: if not, the program should print an error message.

#include <stdio.h>
#include <stdlib.h>

#define COUNTRY_NAME 30

struct dialing_code {
char *country;
int code;
};

Project 16.2

Modify the inventory.c program of Section 16.3 so that the p (print) operation displays the parts sorted by part number.

  • Makefile
  • main.c
  • quicksort.c
  • quicksort.h
  • readline.c
  • readline.h

Project 16.3

Modify the inventory.c program of Section 16.3 by making inventory and num_parts local to the main function.

  • Makefile
  • main.c
  • readline.c
  • readline.h

Project 16.4

Modify the inventory.c program of Section 16.3 by adding a price member to the part structure. The insert function should ask the user for the price of a new item. The search and print functions should display the price Add a new command that allows the user to change the price of a part.

  • Makefile
  • main.c
  • readline.c
  • readline.h

Project 16.5

Modify Programming Project 8 from Chapter 5 so that the times are stored in a single array. The elements of the array will be structures, each containing a departure lime and the corresponding arrival time. (Each time will be an integer, representing the number of minutes since midnight.) The program will use a loop to search the array for the departure time closest to the time entered by the user.

#include <stdio.h>
#include <stdbool.h>

// struct flight {
// int departure_time;
// int arrival_time;
// } flight_time[] = {[0].departure_time = 8 * 60, [0].arrival_time = 10 * 60 + 16,
// [1].departure_time = 9 * 60 + 43, [1].arrival_time = 11 * 60 + 52,
// [2].departure_time = 11 * 60 + 19, [2].arrival_time = 13 * 60 + 31,

Project 16.6

Modify Programming Project 9 from Chapter 5 so that each date entered by the user is stored in a date structure (see Exercise 5). Incorporate the compare_dates function of Exercise 5 into your program.

#include <stdio.h>

/*
* date: A structure that has the members month (int), day (int), and year (int).
*/
struct date {
int month;
int day;
int year;