Book 12: Names for Things

Previous | Next

So far, we’ve learned how to use strings and numbers, and how the computer uses booleans (bool) to represent yes or no as true or false. However, in many cases that is not enough. Often you have concepts that don’t fit into the strict form of true or false, and using a string of text seems awfully wasteful. After all, if you compare two strings, the computer has to compare each character of them against each other.

You could use numbers instead, that would be faster. Simply equate 1 to “yes”, 2 to “no” and 3 to “maybe”. But of course, you’d have to remember this always during coding.

Also, what if you make a typo? One of the nice things about C is that if you type twue instead of true, it will complain. But if you write a string in your code, C will simply consider both the correct and the wrong spelling as valid but different. Same for a number.

That’s why the authors of C invented enumerated symbolic constants, or enums, for short. An enum is simply a name for a number, or a series of numbers. To create an enum, you write it like the following:

enum ThreefoldLogic // Pick any name you like here.
{
    YES = 1,
    NO = 2,
    MAYBE = 3
};

from now on, you can use these three words just like you can use true and false. And C will see them as if you’d typed the numbers they’re equivalent to. But if you mistype one of them, it will not recognize the misspelling and balk at you. Cool, isn’t it? They don’t have to be all-uppercase either. Any valid identifier will do. Whether it be kThisIsMyCoolConstant, or this_is_a_constant.

But there’s more. You can even use this as a type for your variables:

enum ThreefoldLogic   myVar;

myVar = YES;

The nice part about this is that variables declared like this will not accept any other values but the ones in this one enum. So in this example, it’ll accept YES, NO, MAYBE, but not true, false or 5.

Note that these symbolic enumerated constants (sometimes just referred to as “constants”) are limited to being integers or characters (characters are just a different way of writing an integer to the computer)

enum EXAMPLES
{
    A_NEWLINE_CHARACTER = '\n',
    kTab = '\t',
    kFirstCapitalLetter = 'A',
    zero = 0,
    one,
    two
};

Also, as you can see in this example, if you don’t give a number, it will simply add one to the previous constant’s value. This goes so far that you can write:

enum {
    zero,
    one,
    two
};

because when you don’t give a number for the first constant in an enum, it will get the number 0.

An Alternative

One other way of declaring constants that you may encounter a lot is the old preprocessor method:

#define kConstant     15

This is basically just a global search-and-replace. Whatever is on the left side will be replaced with whatever is on the right side. This works even for function calls, but is very low-level. There are a few cases where you need #define, like for defining a constant for a string literal or a floating point number:

#define DEFAULT_DISK_NAME    "Macintosh HD"

And then there are a few more rather advanced uses that won’t be covered here. But in general you should just use enum in your code, because it’s safer.

Typedef

We’ve seen that #define can be used to define synonyms for pretty much everything. However, it is a very simplistic search-and-replace, and as such it is very easy to cause subtle problems. For example, if you wanted to store Mandarin text, which consists of more than 12.000 characters you wouldn’t be able to use the char type for your text strings. So instead, you’d define a new type:

#define ChineseChar    int
#define ChineseString  int*

Now, it would be very easy to make the following mistake:

ChineseChar    charOne,
               charTwo;
ChineseString  stringOne,
               stringTwo;

Which, after the search-and-replace, will turn out as:

int    charOne,
       charTwo;
int*   stringOne,
       stringTwo;

and as you no doubt remember from earlier in this tutorial, you’ve now accidentally declared stringTwo as an int instead of an int*. You’ve essentially hidden away the fact that ChineseString is actually a pointer. To avoid this issue, there is a special command in C, called typedef that you can use to define an alternate name for a type. E.g.:

typedef int*    ChineseString;

If we had used this typedef instead of the #define above, our code would have been equivalent to the following lines of code:

int*       stringOne,
   *       stringTwo;

Because with a typedef, you’ve actually created a completely new type, and this type implicitly is a pointer. So, if you want to define your own names for types, use typedef, not #define to be safe.

While we’re at it, let me mention that typedef can be used for all types. So, you can define your own names for an int to remind people that this int is one of all those Mandarin characters, or you can define an alternative name for a kind of pointer, or even for an enum or a struct:

typedef int    ChineseChar;
typedef int*   ChineseCharPointer;

typedef struct CDDatabaseEntry  CDDatabaseEntry; // Now you can leave away the "struct".

typedef enum ThreefoldLogic  TFL; // Okay, this is too short, although possible.

So, you can even safely use your own names for types to make your code even more readable.

Previous | Next

This entry was posted in C Tutorial. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *


*