Table of Contents

Previous:  5. Problem Solving with Computers

Next: 7. Subprograms


6 Data Structures

Data elements processed by a computer are not limited to characters and numerals. In many applications, a computer may process more complicated data, e.g., customer records, student examination scores, images, and videos. In this chapter, we will discuss other data abstraction and their constructs in C programming language.

6.1 Various Views of an Entity

An entity is an object with certain attributes. For example, a person is an entity who has name, birth date, sex, weight, height, address, phone number, occupation, etc. In a computer, it is difficult, if it is not possible, to fully describe all attributes and features of a person. For a company, it is concerned a person's personnel data, such as name, identification number, employee number, birth date, address, phone, working department, salary, etc. For a police record, a person is recorded his name, identification number, birth date, weight, height, hair color, eye color, etc. For a school, it may be concerned student's name, identification number, department major, courses taken, and grades of those courses. Therefore, an entity may be viewed differently according to the role it plays in a problem.

A programming language usually provides various ways of data representation to represent different views of an entity. The other way around, a view of an entity may be represented in a program using different data representations. We will use an abstract entity called stack to explain the point.

6.2 Data Abstraction: Stacks

A stack can be think of as a container that elements are inserted and removed in the last in first out order. Like a set of numbers, it has addition, subtraction, multiplication, and division operations applied to its elements. A stack is at the empty state initially. Its elements can be of a specific data type such as integers, floating point numbers, or student records. When elements are placed to a stack, the last element is called the top element. A stack has the following operations:

  1. is_empty: check whether the stack is empty,

  2. top: get the element at the top of the stack,

  3. push: place an element to the top of the stack, and

  4. pop: get the top element and remove the top element from the stack.

Stacks are considered as a data abstraction that specifies a conceptual entity that are used in many computer applications. To implement a data abstraction in a program, we need to represent that abstraction using some kind of data structures supported in a programming language. We will continue to introduce more data structures in C programming language.

6.3 Structure Types in C

C supports a number of structured data types: enumerated tags, structures, and unions. These data types are used to represent structural data in different ways.

Enumerated Tags

Enumeration is a way to define a list of named constants. The syntax of enumeration is:

enum identifier {enumeratorList} variable;

where identifier is the name of an enumeration and it is optional and enumeratorList is a list of identifiers that may be assigned values. An enumeration should appears as variable declaration. Consider the following examples of enumeration:

1

2

3

4

enum weekday {Sun, Mon, Tue, Wed, Thu, Fri, Sat};

enum month {Jan=1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec};

enum escapes {Bell='\a', Tab='\t', Newline='\n'};

enum {red=2, green=4, blue=6, yellow, purple=2, orange};

Line 1 specifies a list of weekdays without assigned values. The default values are Sun is 0, Mon is 1, Tue is 2, Wed is 3, Thu is 4, Fri is 5, and Sat is 6. In Line 2, Jan is assigned to 1, and the subsequent names are assigned values as Feb to be 2, Mar to be 3, and, finally, Dec to 12. Line 3 shows that the values of an enumeration can be characters as well. Line 4 specifies integer values to a set of colors. The value of color yellow is 7 which is the integer following color blue. Color purple is assigned to 2 which has the same value of color red 2 and the value of color orange will be 3 which is the integer following 2.

It is possible to declared a variable of enumeration type. However, C programming language does not check whether the value of that variable is in the range of the enumerated tags. For example, in the following program segment, w1 and w2 are declared as two variables of enum weekday.

enum weekday {Sun, Mon, Tue, Wed, Thu, Fri, Sat} w1;

enum weekday w2;

w1 = Sat;

w2 = 8;

Variable w1 and w2 are simply treated as an integer variables. Variable w2 is assigned value 8 which is not in the value range of enum weekday.  Variable w1 is assigned the value of Sat, 6.

Structures

Structure data types are used to represent a collection of elements of different data types. The syntax of structure is as below:

struct identifier {type fieldName1;

                   type fieldName2;

            ¼} variable;

where identifier is the name of a structure and it is optional. A structure may consists of as many fields as specified in the curly brackets. Also, variables can be declared as a struct type. For example, the following declaration declare structure date of three fields and variable birthday is declared of type struct date.

1

2

3

4

5

6

7

struct date {int day;

             int month;

             int year;} birthday;

¡@

birthday.day = 20;

birthday.month = 10;

birthday.year = 1998;

Lines 5 to 7 show that the field of a structure is accessed through a variable of the structure following a '.' and a field name.

Structures can be used to define a new data type as well. Program student_average.c shows the usage of structure data type of representing a student's record including identification number, name, scores of six courses and the average score.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

#include <stdio.h>

typedef struct {
  char id[10];
  char first_name[20];
  char last_name[20];
  int score[6];
  float average;
} student;

int main(void) {

  student stu;
  char *course[6] = {"C Programming for Engineering     ",
                               "Analytical Genometry and Calculus",
                               "Mechanics                                      ",
                               "Mechanics Laboratory                     ",
                               "General Chemistry                          ",
                               "General Chemistry Laboratory        " };
  int creditHour[6] = {3, 3, 3, 1, 3, 1};
  int i, totalScore, totalCreditHour;

  printf("Enter Student ID: ");
  scanf("%s", stu.id);
  printf("Enter Student Name (frist_name last_name): ");
  scanf("%s %s", stu.first_name, stu.last_name);
  printf("Enter Scores for all Courses\n");
  for (i = 0; i < 6; i++) {
    printf("%s ", course[i]);
    scanf("%d", &stu.score[i]);
  }

  printf("\nStudent Id: %s                       Student Name: %s %s\n", stu.id, stu.first_name, stu.last_name);
  totalScore = 0;
  totalCreditHour = 0;

  printf("Course Name Score\n");
  for (i = 0; i < 6; i++) {
    printf("%s: %d\n", course[i],                       stu.score[i]);
    totalScore += stu.score[i] * creditHour[i];
    totalCreditHour += creditHour[i];
  }
  stu.average = (float) totalScore / totalCreditHour;

  printf("Average:                                %5.2f\n", stu.average);

  return 0;
}

Lines 3 to 8 declare a new data type called student. Lines 12 to 17 specify the names of six courses. Line 18 specifies the number of credit hours of the six courses. The program reads student's id number and name, scores of six courses. Lines 21 to 29 read student's id, name, and score. The score report is then produced in the rest of the program. Lines 41 to 46 computes the average score. A sample output is given as below:

Enter Student ID: D0565999
Enter Student Name (frist_name last_name):
John Wang
Enter Scores for all Courses
C Programming for Engineering
80
Analytical Genometry and Calculus
85
Mechanics
90
Mechanics Laboratory
95
General Chemistry
82
General Chemistry Laboratory
85

Student Id: D0565999                   Student Name: John Wang
Course  Name                                   Score
C Programming for Engineering :       80
Analytical Genometry and Calculus:  85
Mechanics :                                        90
Mechanics Laboratory :                      95
General Chemistry :                           82
General Chemistry Laboratory :         85
Average:                                             85.07

The memory space occupied by a structure can be tested using sizeof() function. For example, program size_of_student.c illustrates the size of struct student and how field elements are allocated in memory.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

#include <stdio.h>

typedef struct {
  char  id[10];
  char  name[20];
  int   score[6];
  float average;
} student;

int main(void) {

  student stu;

  printf("Size of studet: %d\n", sizeof(student));
  printf("Address of stu: 0X%X\n", &stu);
  printf("Address of stu.id: 0X%X\n", &stu.id);
  printf("Address of stu.name: 0X%X\n", &stu.name);
  printf("Address of stu.score[0]: 0X%X\n", &stu.score[0]);
  printf("Address of stu.average: 0X%X\n", &stu.average);


  return 0;
}

The following output shows that an instance of struct student takes 60 bytes. The instance starts at addresss 0X22FF40. Field stu.id occupies 10 bytes, from 0X22FF40 to 0X22FF49 and stu.name occupies 20 bytes, from 0X22FF4A to 0X22FF5D. Field stu.score is an integer array of six elements, i.e., 24 bytes, and each integer takes four bytes. Since an integer data must start with the beginning of a word, two bytes of memory space, 0X22FF5E and 0X22FF5F, are not used. The starting address of stu.score[0] becomes 0X22FF60. Finally, stu.average is a floating number which takes four bytes from 0X22FF78 to 0X22FF7B. As a result, the memory space for student stu starts from address 0X22FF40 to 0X22FF7B, total of 60 bytes.

Size of student: 60
Address of stu: 0X22FF40
Address of stu.id: 0X22FF40
Address of stu.name: 0X22FF4A
Address of stu.score[0]: 0X22FF60
Address of stu.average: 0X22FF78

Structures can be nested, i.e., a structure inside another structure. In program nested_structure.c, student's name is further divided into last name and first name which is represented as a structure (Lines 5 to 8). Access to fields of a nested structure is through multiple dotted expression, e.g., stu.name.last and stu.name.first.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

#include <stdio.h>

typedef struct {
  char id[10];
  struct {
    char first[20];
    char last[20];
  } name;
  int score[6];
  float average;
} student;

int main(void) {

  student stu;

  printf("Enter Student's Fisrt Name¡G ");
  scanf("%s", stu.name.first);
  printf("Enter Student's Last Name¡G ");
  scanf("%s", stu.name.last);

  printf("Student Name: %s %s\n", stu.name.first, stu.name.last);


  return 0;
}

A sample run of the program is shown below:

Enter Student's Fisrt Name:  John
Enter Student's Last Name:
 Wang
Student Name:  John Wang

Unions

Type union in C programming language is a data type to represent various data fields but sharing the same location. The syntax of union is similar to that of struct:

union identifier {type fieldName1;

                  type fieldName2;

                   ¼} variable;

Consider program union_type.c. Vairable num is declared as a union number_union type of three fields num.integer_num, num.unsigned_num, and num.float_num of integer, unsigned, and float type, respectively. Initially, these fields are set to 0. Only num.integer.num is modified to -1 (Line 15).

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

#include <stdio.h>

int main(void) {

  union number_union {
    int      integer_num;
    unsigned unsigned_num;
    float    float_num;
  } num;

  num.integer_num = 0;
  num.unsigned_num = 0;
  num.float_num = 0;

  num.integer_num = -1;
  printf("Integer: %d\n", num.integer_num);
  printf("unsigned: %u\n", num.unsigned_num);
  printf("float: %f\n", num.float_num);

  printf("address of num.integer_num: 0X%X\n", &num.integer_num);
  printf("address of num.unsigned_num: 0X%X\n", &num.unsigned_num);
  printf("address of num.float_num: 0X%X\n", &num.float_num);

¡@

  return 0;

}

However, the output from Lines 16 to 18 show that num.unsigned_num and num.float_num are also modified because these three fields share the same location as indicated by the output from Lines 20 to 22.

integer: -1
unsigned: 4294967295
float: -1.#QNAN0

address of num.integer_num: 0X22FF7C
address of num.unsigned_num: 0X22FF7C
address of num.float_num: 0X22FF7C

The fields of a union type may be data types of various size. In program union_sizeof.c, union number_union has four fields, each of the fields is of type char, int, float, or double. Function sizeof() is called with an expression, i.e., a variable or a variable with field access.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

#include <stdio.h>

int main(void) {

  union number_union {
    char   character_num;
    int    integer_num;
    float  float_num;
    double double_num;
  } num;

  printf("size of num: %d\n", sizeof(num));
  printf("size of num.character_num: %d\n", sizeof(num.character_num));
  printf("size of num.integer_num: %d\n", sizeof(num.integer_num));
  printf("size of num.float_num: %d\n", sizeof(num.float_num));
  printf("size of num.double_num: %d\n", sizeof(num.double_num));

¡@

  return 0;

}

The output of Line 12 shows the size of union number_union is eight bytes which is the largest memory space required of each of the four fields, num.double_num.

size of num: 8
size of num.character_num: 1
size of num.integer_num: 4
size of num.float_num: 4
size of num.double_num: 8


Table of Contents

Previous:  5. Problem Solving with Computers

Next: 7. Subprograms