Author : Tom Torfs
Page : << Previous 7 Next >>
with int variables.
[*] That doesn't necessarily mean that enums and ints will be exactly the same type, but they are compatible types
So we could also have defined the variables like this:
int sky, forest;
And still have used the assignment of enum constants below.
Another consequence of this equivalence is that there is no special printf() format specifier to print an enum value. We have to use the format specifier for an integer: %d. An enum constant needn't even be cast as it is guaranteed to be of type int.
printf("There are %d colours in the enum\n", NUMBER_OF_COLOURS);
This also means that when we print an enum value, a number will be output, not the symbolic name.
sky = BLUE;
forest = GREEN;
Here we assign values to the variables using our defined constants. These statements are equivalent to:
sky = 4;
forest = 3;
But obviously that's much less clear, and when the enum's definition is modified this code may no longer be equivalent to the above.
printf("sky = %d\n", (int)sky);
printf("forest = %d\n", (int)forest);
The values of the enum variables are printed, again as integers. [*] However, in this case we have typecast the enum to int first. This is because, although unlikely, the compiler may have chosen another (larger) type for the enum itself (not the constants).
[*] If you would really like the symbolic names to be printed, you'll have to write code to that yourself. You may want to use either an array of strings (see 10.2. Arrays and strings) or a switch() statement (see 8. switch/case/default).
6. Structs, unions and bit-fields
6.1. The cars program
/* prog6-1.c: cars */
enum brands {VOLKSWAGEN, FORD, MERCEDES, TOYOTA, PEUGEOT};
enum colours {RED, GREEN, BLUE, GREY, BLACK, WHITE};
struct car {enum brands brand;
enum colours colour;
int second_hand;
union {unsigned long mileage; int months_guarantee;} carinfo;
unsigned diesel:1, airbag:1, airconditioning:1;
};
int main(void)
{
struct car mycar = {VOLKSWAGEN, GREY, 1, 50000, 0, 0, 0};
struct car yourcar;
yourcar.brand = MERCEDES;
yourcar.colour = BLACK;
yourcar.second_hand = 0;
yourcar.carinfo.months_guarantee = 12;
yourcar.diesel = 1;
yourcar.airbag = 1;
yourcar.airconditioning = 1;
return 0;
}
This program has no output.6.2. Using structs, unions and bit-fields
These lines should look familiar: (see 5. Enums and typedefs)
enum brands {VOLKSWAGEN, FORD, MERCEDES, TOYOTA, PEUGEOT};
enum colours {RED, GREEN, BLUE, GREY, BLACK, WHITE};
But the next few lines contain several new things:
struct car {enum brands brand;
enum colours colour;
int second_hand;
union {unsigned long mileage; int months_guarantee;} carinfo;
unsigned diesel:1, airbag:1, airconditioning:1;
};
OK, this is the definition of a structure type. By writing something of this kind:
struct structure_tag {type1 member1; type2 member2; /* etc. */ };
You define a structure type which from this point on you can use to define structure variables, e.g.: [*]
struct structure_tag mystructure; /* don't forget the struct keyword */
[*] This could be written in one line as struct structure_tag {type1 member1; type2 member2;} mystructure; (in which case the structure_tag is optional)
A structure is a collection of different variables (called members) into a single type. It is usually used to describe all properties of a certain item in a single variable. In our cars example we could store information like the brand, colour etc. in a different variable for every car. But it is a lot more clear to concentrate all the information about one specific car into a type of its own, analogous to e.g. Pascal records. That's why a structure is used.
The first two members are familiar enums:
enum brands brand;
enum colours colour;
The next variable:
int second_hand;
Is used to store whether the car is a second hand car or a new car. In the former case a 1 is stored in this member variable, in the latter case a 0.
But the information that we need to store about a second hand car needn't be the same as that for a new car. For example, we only need to store the mileage information for second hand cars and guarantee information for new cars. So sometimes it may be a good idea to not have unneeded information take up space in our structures. That's why unions are available:
union {unsigned long mileage; int months_guarantee;} carinfo;
This is a union definition inside our struct definition (you can put structs within structs etc.). The union definition looks very similar to a struct definition. There is one very important difference though: in a struct space is allocated for every member. In a union enough space is allocated so that the largest member can be stored (and therefore the other members as well). But only one member can hold a value at a time; by assigning a value to one member of a union the values of the other members of a union are destroyed. You can only use the value of the union member that a value was last assigned to. [*]
[*] Many systems allow reading other members than the one last assigned to, but this is not standard.
Because of this, we have to keep track of whether the mileage member or the months_guarantee member contains a valid value. This can be determined by looking at the second_hand variable. If that is 1, the mileage member contains a valid value; if that is 0, the months_guarantee member contains a valid value.
unsigned diesel:1, airbag:1, airconditioning:1;
Sometimes member variables in a structure only need to store a few values, often only 1 or 0 which would fit in a single bit. To avoid having to allocate an entire int or char for every such variable, you can use bit-fields. The above definition will allocate an unsigned int and define three variables (diesel, airbag and airconditioning) of each 1 single bit wide. [*]
[*] Bit-fields should always be of type signed int or unsigned int. If you use plain int, it is implementation-defined whether they'll be signed or unsigned. Many systems support other types, but this is not standard.
OK, now we move into the body of main():
struct car mycar = {VOLKSWAGEN, GREY, 1, 50000, 0, 0, 0};
struct car yourcar;
As mentioned above, these are definitions of two struct car variables, called mycar and yourcar. What's special is that mycar is initialized. This is comparable to how regular variables are initialized, except for the form of the initializer. For a struct this should be between { and } braces, and initializer values for the members should be separated by commas. For a union (such as our carinfo member, which may contain either mileage or months_guarantee information) it is always the first member that is initialized, so in our case the 50000 will be used to initialize mileage, not months_guarantee. Bit-fields are initialized like normal member variables.
yourcar.brand = MERCEDES;
yourcar.colour = BLACK;
yourcar.second_hand = 0;
yourcar.carinfo.months_guarantee = 12;
yourcar.diesel = 1;
yourcar.airbag = 1;
yourcar.airconditioning = 1;
Each member of yourcar is initialized manually. A member of a structure is accessed by appending .membername to the structure variable name. The same goes for a union, that's why we have to use yourcar.carinfo.months_guarantee in the fourth line instead of simply yourcar.months_guarantee.
The reason yourcar can't be initialized in the same way as mycar is that, as mentioned above, we can only initialize the first member variable of a union in that way, not the others (in this case the months_guarantee member variable).
7. Conditionals (if/else)
7.1. The yesno program
/* prog7-1.c: yesno */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
int yesno;
srand((unsigned)time(NULL));
yesno = rand() % 2;
if (yesno==0)
printf("No\n");
else
printf("Yes\n");
return 0;
}
This program outputs either:
No
or
Yes
7.2. Obtaining random numbers
Although this chapter is about using conditionals, we're
Page : << Previous 7 Next >>