And last but not least – arrays of structs.

Of course there is one final thing – creating arrays of struct. Most often, a struct will not exist in isolation, you may require a multitude of them. For example, a book struct is useful, but an array of them is even more constructive – this makes a library. There are of course a number of ways of doing this.

1. Using a simple array of struct

So if we use this struct:

struct bookinfo {
    char title[50];
    char author[50];
    char subject[100];
    char isbn[11];
    int year;
};

we can then create an array of struct where it is needed, for example main():

struct bookinfo cooking[100];

This creates an array, cooking with 100 elements of type bookinfo. Individual members of each element can now be assigned in the following manner:

strcpy(cooking[0].title, "Fun with Foie Gras");
cooking[0].year = 2015;

Function parameters can also be modified to allow for the use of this array of structs:

void enterBookdata(struct bookinfo b[], int index)
{
    printf("Book title? ");
    fgets(b[index].title, 50, stdin);
    printf("Book author? ");
    fgets(b[index].author, 50, stdin);
    printf("Book subject? ");
    fgets(b[index].subject, 100, stdin);
    printf("Book ISBN? ");
    fgets(b[index].isbn, 11, stdin);
    printf("Year of publication? ");
    scanf("%d", &b[index].year);
}

which could be called in the following manner (from main()):

enterBookdata(cooking,0);

2. Using an array created using a typedef

The second approach may be more convenient, especially when it comes to creating function parameters. Using the same struct as above, then a typedef which is an array of struct can be defined:

typedef struct bookinfo library[100];

Now in main(), a variable can be defined:

 library cooking;

The function will now look like this:

void enterBookdata(library b, int index)
{
    printf("Book title? ");
    fgets(b[index].title, 50, stdin);
    printf("Book author? ");
    fgets(b[index].author, 50, stdin);
    printf("Book subject? ");
    fgets(b[index].subject, 100, stdin);
    printf("Book ISBN? ");
    fgets(b[index].isbn, 11, stdin);
    printf("Year of publication? ");
    scanf("%d", &b[index].year);
}

which could be called in the following manner (from main()):

enterBookdata(cooking,0);

Note that because BOTH approaches use arrays, there is no necessity to define a pass-by-reference parameter in the function  in order to modify the contents of each of the structs in the array.

If the struct created contains dynamic members, then of course the complexity of what is to be done will increase.

 

 

 

 

 

Advertisements

What is a typedef?

So in C, apart from the struct, there is also an entity known as a typedef. typedef allows complex types to be created, and assign simple names to them. In some cases it is really creating a synonym. For example:

typedef unsigned char byte;

This creates a “new” type named byte, which is essentially an unsigned char which can contain the values 0-255. Variables of this “type” can now be defined:

byte b1, b2;

It is also possible to use a typedef to create a type from a struct. For example consider the following struct:

struct image {
    int nrow;
    int ncol;
    int pixel[700][500];
};

To create a variable from the struct image, or to define it as a parameter in a function requires the use of the clause struct in front of image, which can become tedious. For example:

struct image p;

This declares a variable p, which is an image struct. By defining a typedef we remove this restriction. Now there are a number of ways of creating them. The first is to use an existing struct, as above, and associat a typedef with it. For example:

typedef struct image photo;

This defines photo as a “type” which is a struct. Now declaring a variable p, can be done as follows:

photo p;

The second way is to associate the typedef when the struct is created. For example:

typedef struct image {
    int nrow;
    int ncol;
    int pixel[700][500];
} photo;

Now photo can be used in the same way. It is *also* possible to drop the name of the struct from the definition:

typedef struct {
    int nrow;
    int ncol;
    int pixel[700][500];
} photo;

This is useful when using structs as parameters to functions. For example, a function that prints out the data in the struct image could be defined as:

void printImage(struct image p)

or using the typedef as:

void printImage(photo p)

which is somewhat simpler.

Here’s another example:

struct bookinfo {
    char title[50];
    char author[50];
    char subject[100];
    char isbn[11];
};
typedef struct bookinfo Library[1000];

Here the struct contains information about a book. What the typedef does is create an array of structs with 1000 books, as a new “type”. We could then create a Library called cooking in the following manner:

Library cooking;

So cooking contains 1000 books.

 

 

 

 

What about nested structs?

There is also the ability to make a nested struct. One way is to declare a struct then use that struct inside another struct. For example:

struct date {
    int day;
    int month;
    int year
};
struct bookinfo {
    char title[50];
    char author[50];
    char subject[100];
    char isbn[11];
    struct date pubdate;
};

Then assigning a value to the year would occur as follows:

struct bookinfo b;
b.pubdate.year = 2015;

The second way is embedding a struct within a struct:

struct bookinfo {
    char title[50];
    char author[50];
    char subject[100];
    char isbn[11];
    struct date {
        int day;
        int month;
        int year;
    } pubdate;
};

Which works in the same way.

 

structs and functions

Now that we have the basics of using a struct, what about passing them to a function?

It is possible to pass instances of the struct to a function, either by pass-by-value or pass-by-reference, OR, by returning the struct.

Let’s use the following struct as before, and typedef it to create a “type” named book, and explore all three methods.

struct bookinfo {
    char title[50];
    char author[50];
    char subject[100];
    char isbn[11];
    int year;
};
typedef struct bookinfo book;

1. Passing by value

If the data contained in a struct is to be used within a function, but not modified, then is can be defined as pass-by-value. For example the following function printfBookdata(), prints out the book data.

void printBookdata(book b)
{
    printf("Book information:n");
    printf("Title   : %s\n", b.title);
    printf("Author  : %s\n", b.author);
    printf("Subject : %s\n", b.subject);
    printf("ISBN    : %s\n", b.isbn);
    printf("Year    : %d\n", b.year);
}

ddd

2. Passing by reference

If the data contained in a struct is to be modified within a function, then it should be defined as pass-by-reference. For example, the following function enterBookdata() takes an empty book, and “populates” it with data.

void enterBookdata(book *b)
{
    printf("Book title? ");
    fgets(b->title, 50, stdin);
    printf("Book author? ");
    fgets(b->author, 50, stdin);
    printf("Book subject? ");
    fgets(b->subject, 100, stdin);
    printf("Book ISBN? ");
    fgets(b->isbn, 11, stdin);
    printf("Year of publication? ");
    scanf("%d", &b->year);
}

This function is called in the following manner:

book abook;
enterBookdata(&abook);

Note that there are some differences in the way things are structured inside the function. Because the book, b is a pointer to the struct, “->” has to be used instead of “.” to qualify the members.

3. Passing information back using return

The final way of returning things is by using the return statement. Here is the same function as above modified to account for this scenario.

book enterBookdata()
{
    book b;

    printf("Book title? ");
    fgets(b.title, 50, stdin);
    printf("Book author? ");
    fgets(b.author, 50, stdin);
    printf("Book subject? ");
    fgets(b.subject, 100, stdin);
    printf("Book ISBN? ");
    fgets(b.isbn, 11, stdin);
    printf("Year of publication? ");
    scanf("%d", &b.year);
 
 return b;
}

Note that a local struct, b,  is created. It is then populated (and as it is local it uses the “.” separator). Then the struct is returned. The calling end of the function looks like this:

book b;
b = enterBookdata();

A pointer is not needed in this case.

 

Simple structs in C

Elementary data structures in any language are only convenient if the complexity of the problem being solved is low. Once data begins to become more complex, larger frameworks for encapsulating it are needed. This makes it easier to transfer data between subroutines, and essentially binds similar pieces of data together. In C, one such structure is aptly called a struct.

You don’t need to know anything about object-oriented programming, move along,

C is not an object-oriented language, so structs have nothing to do with objects. A struct is a type used to represent a heterogeneous collection of data, essentially a data aggregate. It is a mechanism for treating a set of different types as a single, coherent unit. For example, say we wanted to create a struct to hold data from a photograph (grayscale, with values 0-255):

struct image {
    int nrows;
    int ncols;
    int pixel[500][500];
    char date[9];
    int aperture;
    int ISOspeed;
};

This struct, named image contains a lot of information about the photograph, all encapsulated into one nice package, the constituent items of which are called members. Okay, so the storage associated with pixel seems kinda of small, at ¼ megapixel – but if you go to 1000×1000, at 1 MP this will be too large to store in a stack (and will require a dynamic array using the heap). It is then possible to create a variable, photo,  from the struct:

struct image photo;

Assigning data to the individual members can be easily performed:

photo.nrows = 400;
photo.ncols = 400;
strcpy(photo.date,"06111963");

But take note of the following. A struct declared as:

struct image {
    ...
}

is not the same as:

struct {
    ...
} image;

The first creates a struct named image, which can then be used to declare struct variables. The second defines image as a variable with this struct definition, but the definition cannot be re-used. If we try to declare a variable photo from this:

image photo;

The compiler will likely return a warning followed by an error, along the lines of:

warning: statement with no effect
error: expected ‘;’ before ‘photo’

More on that in the next post on typedef.

 

Algol compiler with some interesting feedback

Writing some code in Algol this morning (or at least trying to). I knew it wasn’t right, but here’s what the compiler threw back at me:

a68g: syntax error: a formula etcetera is not a valid construct.

Okay. Interesting. The warning is even better:

a68g: warning: skipped superfluous semi-symbol.

I almost want to write bad programs to see what other gems the compiler will throw back at me.

Dijkstra on APL

“APL is a mistake, carried through to perfection. It is the language of the future for the programming techniques of the past: it creates a new generation of coding bums.”

Edsger Dijkstra

Sally sells C strings by the C shore

Is there anything that quantifies evil in C better than strings?

Let’s look at the following piece of C code:

char aString[10];
scanf("%s", aString);
printf("%s\n", aString);

Now if we try and input an ASCII string “thecatsat, nothing goes wrong because the string is 9 characters in length, and will fit into the character array aString with no issue. Now try “thecatsatonthemat“. Depending on the system you’re running it on, this won’t cause any problems, which is an issue. Now when we run it with “thecatsatonthemat1” (on OSX), it returns with an “Abort trap: 6”. On another Unix system it doesn’t baulk until I type “thecatsatonthemat1234567“, then it produces a segmentation fault. The problem is that there is only “official” storage for 9 characters and the EOS (End-Of-Sting)  terminator ”. But C allows this fiasco to go on.

And the compiler won’t find these sort of bugs either. But it gets worse. Now we code something like this:

char aString[10];
char bString[10] = "1234567890";

scanf("%s", aString);
printf("String A: %s\n", aString);
printf("String B: %s\n", bString);

Notice that the string bString is initialized with exactly 10 characters, leaving no room for the EOS terminator. What happens when this compiles (OSX)? Nothing, there are no complaints. What happens when it runs, and we enter “thecat”? Here’s the output:

String A: thecat
String B: 1234567890thecat

Somehow the lack of string terminator has appended the memory location of aString to bString. On the other Unix system it did nothing. Make the initialized string the right length, and it works fine. Make it too long, and the compiler will complain, with a message something along the lines of:

warning: initializer-string for array of chars is too long

What is obvious is that C can’t be trusted to find coding inaccuracies for you. Other languages do strings better. In Fortran, a string is declared in the following manner (and is different from a character array):

character (len=10) :: str

Then if the “thecatsatonthemat” is input, only the first 10 characters,  “thecatsato” is stored – the rest is discarded. Fortran protects from the craziness that C doesn’t. And it doesn’t make the programmer deal with the EOS thing, if fact Fortran strings are not null-terminated.

 

AI in Sci-Fi robots

Let’s ponder the evolution of artificial intelligence in science fiction robots. Robby the Robot appeared in the 1956 movie Forbidden Planet, heralding the first appearance of a robot. Ironically in the same year that the term “artificial intelligence” was coined at Dartmouth. Robby was programmed by his creator Dr. Morbius to obey Isaac Asimov’s Three Laws of Robotics.  These laws specify that a robot (i) may not injure a human being, (ii) must obey human orders and (iii) must protect its own existence. Robby was built to serve mankind and is capable of cooking, cleaning, sewing, driving and manufacturing booze. Robby was anthropomorphic, that is having human qualities, had the sophistication of  human being, and even a dry wit. Robby represented the future of robots, and the AI that ran them. After Robby came the Robot from TV series Lost in Space (1965-1968). The robot had no name, but was a Class M-3 Model B9 – General Utility Non-Theorizing Environmental Control Robot. The Robot was capable of complex calculations, had sensors capable of detecting danger (which he was extremely adept at, and anyone who knows the series knows the catchcry “Danger, Will Robinson!“), and vast repositories of information. The robot boasted having “the best computer on earth”, and its speech interpretation abilities were extremely proficient. When it couldn’t do something, it used the phrase “does not compute”.

In the 1970s came the two droids from Star Wars: R2-D2 ad C-3PO. The Star Wars movies represent an realm where computers are inherently intelligent, and droids are task-specific. Notice the use of the term droid.  Yet neither have the ability to divert from their intrinsic programming. R2-D2 is an astromech droid, capable of almost anything it seems, including hacking. C-3PO’s main task is protocol, and has the ability to translate 3,000,000 different languages. Yet these droids are service tools, programmed to do a job. Their intelligence stems from a plethora of information gathered though their interactions, essentially learned behaviour. Whilst they are capable improvisation within the scope of their abilities, they more often than not stick to their pre-programmed notions. R2 units for example, were designed to make repairs to whatever starship they were assigned to, yet R2-D2 used his experiences to learn and grow, so it is possible that all such units had this capability, bar memory wipes. But in reality, these droids are more akin to appliances than artificial life. True, they do mimic human behaviour on occasion, but do they have a soul? Do they feel? Do they have hopes and dreams like sentient beings do?

Some might consider the phrase C3PO’s cries out when Vader attacks Leia’s ship at the start of Episode IV:  “We’re doomed“. Is this a gut feeling he has? Or more likely a calculation of the firepower of Vader’s ship over the Tantive IV.

Lastly we come to our friends the “Cylons”, or Battlestar Galactica fame. Appearing first in the 1978 TV series, the metallic version of these robots are not in any great sense of the word thinkers. They follow a set modus operandi, taking orders from their more intelligent human-like keepers. The idea of such robots is not inconceivable, as battlefield robotics is certainly on the radar. The human-like cylons, also known as skin-jobs, are another matter all together. It is hard to conceive that they are robotic at all, because they are entirely composed of biological matter. They eat, they sleep, they think. They have a conscience, and although they can share their memories to the collective, this may be more of a limitation on our part than anything else. They are essentially genetically modified humans, and as such don’t really have a truly artificial intelligence.

 

The Life of Variables.

These entities can be found everywhere in programs, from those who live a more global existence, to others found in more secluded,  localized environs. All lead a busy yet tumultuous life, coming to life when a function is put into memory, and vanishing when that function ends.

Life for variables it seems, is precarious.

Variables, or rather the entities that store data in a program usually have some form of lifespan, i.e. they are created, and ultimately destroyed. If they persisted forever then when a program ends, the memory would be “lost”, until such time as the physical memory fills up and the system crashes – not an ideal scenario! So what does scope mean?

The scope of a variable is the region of the program in which statements may refer to that variable, or change its contents. Consider the example program below:

1   #include <stdio.h>
2
3   int x=7;
4   int main(void)
5   {
6       double y;
7       y = 1.2;
8       int z;
9       z = x; 
10      return 0;
11  }

The variable x is considered a global variable. Global variables are declared in the region following the pre-processor statements, but before the main function is declared. The whole program has access to these variable. Both variables y and z are considered local variables. They can only be used within main, and only in the order in which they are declared. So the variable y is declared, and is assigned the value 1.2 (on line 7). It would not be possible to assign y the value of z on line 7, because z has not yet been declared. However assigning z the value of x on line 9 is possible, because x is a global variable.

NOTE: In general you should avoid using global variables due to the fact that they can potentially be modified from anywhere in the program. There is the possibility of the value if a global variable changing unexpectedly, resulting in a subtle error which can be extremely difficult to locate, since the offending line could be anywhere in the program.