CS140 Lecture notes -- Structs

  • Jim Plank
  • Directory: ~cs140/www-home/notes/Structs
  • Lecture notes: http://www.cs.utk.edu/~cs140/notes/Structs
  • Thu Sep 10 10:21:43 EDT 1998

    Structs

    Structs are a second way (arrays being the first) of aggregating types. For example, if you want to keep information (such as first name, last name, age) on a person, you would probably use a struct, so that all the information on the person is in one place.

    Look at struct1.c:

    #include < stdio.h >
    #include < string.h >
    
    typedef struct {
      char *fname;
      char *lname;
      int  age;
    } Person;
    
    print_person(Person *p)
    {
      printf("%s %s: Age: %d\n", p->fname, p->lname, p->age);
    }
    
    main()
    {
      char line[1000];
      Person p;
    
      printf("Enter first name: "); 
      if (gets(line) == NULL) exit(0); 
      p.fname = strdup(line);
    
      printf("Enter last name: "); 
      if (gets(line) == NULL) exit(0); 
      p.lname = strdup(line);
    
      printf("Enter age: "); 
      if (gets(line) == NULL) exit(0); 
      p.age = atoi(line);
    
      print_person(&p);
    }
    
    Note the typedef statement. This means that you can use the name Person to access the struct. Otherwise, you'd have to name the struct with something like:
    struct person {
      char *fname;
      char *lname;
      int  age;
    };
    
    And then you'd name variables such as p with ``struct person p''. If this is not review for you, look at struct1a.c, which is equivalent to struct1.c without the typedef.

    Now, struct1 statically allocates one Person struct, and then fills it in with info from standard input. Note I'm using gets() instead of scanf(). This is just a matter of preference -- soon we won't be using either of them.

    Then print_person() is called to print out the struct. Note, if I have a struct (such as p in main()), then I access its fields with a dot. If I have a pointer to a struct (such as p in print_person()), then I access its fields with an arrow (->). Note how I called print_person() with the address of p. You should be getting used to this by now.

    You will typically be dealing with pointers to structs, so get used to using the arrows rather than the dots.


    Struct2.c

    Struct2.c shows a more typical use of structs:
    #include < stdio.h >
    #include < string.h >
    
    typedef struct {
      char *fname;
      char *lname;
      int  age;
    } Person;
    
    print_person(int i, Person *p)
    {
      printf("Person %3d: %-20s %-20s Age: %3d\n", i, p->fname, p->lname, p->age);
    }
    
    main()
    {
      char line[1000];
      Person *parray;
      int n, i;
    
      printf("How many people will you enter? ");
      if (gets(line) == NULL) exit(0); 
      n = atoi(line);
      if (n <= 0) exit(0);
    
      parray = (Person *) malloc(sizeof(Person) * n);
    
      for (i = 0; i < n; i++) {
        printf("Enter first name of person %d: ", i); 
        if (gets(line) == NULL) exit(0); 
        parray[i].fname = strdup(line);
      
        printf("Enter last name of person %d: ", i); 
        if (gets(line) == NULL) exit(0); 
        parray[i].lname = strdup(line);
      
        printf("Enter age of person %d: ", i); 
        if (gets(line) == NULL) exit(0); 
        parray[i].age = atoi(line);
      } 
    
      for (i = 0; i < n; i++) {
        print_person(i, &(parray[i]));
      }
    }
    
    Instead of reading in one person , we prompt the user for a number of people to read in, and then we read in that many people. In order to have this work with any number of people, we must use malloc() to allocate an array with the appropriate number of people. Then we fill it in, just as struct1.c. Finally, we traverse the array and print each person. Note how I make the call to print_person(). The following also would have worked:
      print_person(i, parray+i);
    
    Personally, I like &(parray[i]) because it states more plainly what you're doing, but both ways work.

    Also, look at print_person(). I have put field widths into the printf() statement. The format string says to:

    Try it out:
    UNIX> struct2
    How many people will you enter? 3
    Enter first name of person 0: Jim 
    Enter last name of person 0: Plank
    Enter age of person 0: 32
    Enter first name of person 1: Brad
    Enter last name of person 1: Vander Zanden
    Enter age of person 1: 35
    Enter first name of person 2: Katie
    Enter last name of person 2: Plank
    Enter age of person 2: 4
    Person   0: Jim                  Plank                Age:  32
    Person   1: Brad                 Vander Zanden        Age:  35
    Person   2: Katie                Plank                Age:   4
    UNIX> 
    
    Note that by using gets() I was able to handle that two-word last name.

    Struct3.c

    Struct3.c tweaks struct2.c so that parray is now an array of pointers to Person structs, rather than an array of Person structs.
    #include < stdio.h >
    #include < string.h >
    
    typedef struct {
      char *fname;
      char *lname;
      int  age;
    } Person;
    
    print_person(int i, Person *p)
    {
      printf("Person %3d: %-20s %-20s Age: %3d\n", i, p->fname, p->lname, p->age);
    }
    
    main()
    {
      char line[1000];
      Person **parray, *p;
      int n, i;
    
      printf("How many people will you enter? ");
      if (gets(line) == NULL) exit(0); 
      n = atoi(line);
      if (n <= 0) exit(0);
    
      parray = (Person **) malloc(sizeof(Person *) * n);
    
      for (i = 0; i < n; i++) {
    
        p = (Person *) malloc(sizeof(Person));
        parray[i] = p;
    
        printf("Enter first name of person %d: ", i); 
        if (gets(line) == NULL) exit(0); 
        p->fname = strdup(line);
      
        printf("Enter last name of person %d: ", i); 
        if (gets(line) == NULL) exit(0); 
        p->lname = strdup(line);
      
        printf("Enter age of person %d: ", i); 
        if (gets(line) == NULL) exit(0); 
        p->age = atoi(line);
      } 
    
      for (i = 0; i < n; i++) {
        print_person(i, parray[i]);
      }
    }
    
    Each Person struct is now allocated right before being filled in. I think that this code looks cleaner than struct2.c, but of course, individual tastes do vary.