Learning C by comparison #2: Arrays, and more on structs

This is the second part of my Learning code by comparison series of learning C with explanations in Ruby.

This time around I’m going to answer some simple questions I’ve had for myself while learning basic C. The first thing is how I could call a struct from another:

In Ruby I can do this:


class Person
  attr_accessor :name, :age, :colors

  def initialize(name, age, eye_color, hair_color)
    self.name   = name
    self.age    = age
    self.colors = Colors.new(eye_color, hair_color)
  end
end

class Colors
  attr_accessor :eyes, :hair

  def initialize(eye_color, hair_color)
    self.eyes = eye_color
    self.hair = hair_color
  end
end

person = Person.new('James Newton', 21, 'brown', 'brown')
person.name        #=> "James Newton"
person.colors.hair #=> "brown"
      

Since we don’t have fancy classes in C as I touched on in the last post, we use struct to define our “objects” in C:


#include <stdio.h>

typedef struct {
    char *eyes;
    char *hair;
} Colors;

typedef struct {
    char   *name;
    char   *age;
    Colors colors;
} Person;

int main(int argc, const char *argv[])
{
    Person person = {"James Newton", 21, {"brown", "brown"}};

    printf("Name: %s, Age: %i, Eye color: %s, Hair color: %s\n", person.name, person.age, person.colors.eyes, person.colors.hair);

    return 0;
}
      

And after a quick compile and run we’ll see:


$ gcc test.c
$ ./a.out
Name: James Newton, Age: 21, Eye color: brown, Hair color: brown
      

Now, how to work with arrays!

Ruby is easy, array = ['one', 'two', 'three'] gives us an array of three strings we could access with array[i]. With C we get a little complicated depending on what information we’re throwing in our array.


#include <stdio.h>

int main(int argc, const char *argv[])
{
    char *people[3] = {"James Newton", "Robert Babcock", "Zach Smith"};

    for (int i = 0; i < sizeof(people) / sizeof(people[0]); i++) {
      printf("Name: %s\n", people[i]);
    }

    return 0;
}
      

We can also define people[3] one by one like:


char *people[3];

people[0] = "James Newton";
people[1] = "Robert Babcock";
people[2] = "Zach Smith";
      

Since we’re doing a loop here to print out all the names in people we’re using sizeof to get the size of people in bytes, then dividing it by the size of an item in the array (people[0]). Without this the loop would continue to run past the 3 items because the array is greater than 3 bytes, and segfault. Ruby handles this for us when looping through arrays.

Compiling and running would get us:


$ gcc test2.c
$ ./a.out
Name: James Newton
Name: Robert Babcock
Name: Zach Smith
      

So now that we have an idea of how to use struct, why not try to make an array of “objects”?

Adding on to our Ruby script from earlier, we could do this quickly:


people = []
people << Person.new('James Newton', 21, 'brown', 'brown')
# add more people, like Robert and Zach.

people.first.name #=> "James Newton"
      

Now we’ll make use of the code from both of our previous C programs to make an array called people that contain several Person struct’s.


#include <stdio.h>

typedef struct {
    char   *name;
    int    age;
} Person;

int main(int argc, const char *argv[])
{
    Person people[2] = {{"James Newton", 21}, {"Robert Babcock", 22}};

    for (int i = 0; i < sizeof(people) / sizeof(Person); i++) {
      printf("Name: %s, Age: %i\n", people[i].name, people[i].age);
    }

    return 0;
}
      

And when we compile we get the following output:


$ gcc test3.c
$ ./a.out
Name: James Newton, Age: 21
Name: Robert Babcock, Age: 22
      

And we’ll stop here. So with this we’ve got some very simple samples on how to use a struct inside of another struct, and arrays.

Full code samples.