macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Learning Cocoa: Repurposing Variables

by Seth Roby
01/13/2004

Joey from Friends is the slow one. Tim Curry seems to be the bad guy in all his new movies. And Macaulay Culkin will forever be the kid from Home Alone. We, the audience, have come to know these people in these roles, and the actors can do little to escape their fate: they will forever be typecast.

The variables in your Cocoa program have it a little easier. Today we’ll see a few new powerful ways we can use our variables to do things we never could before, building on the concepts we learned in the last lesson.

Bits of Knowledge

By now we know that every variable is really just a bunch of bits, which we can look at a certain way and read as an integer, a pointer, or whatever else we need to remember. Well, what else do we want to remember?

We noted when we learned about int that an integer can’t hold any decimal places, so we can’t shove pi into an int. There’s a different data type to put such numbers in, and it’s called a float, because it stores one “floating-point” number, so named because the decimal point can be anywhere inside the number, floating about between any two digits, not trapped to the right of the ones column like it is in an int.

Let’s look at some simple code for using floats; I’ve upgraded the previous main method to allow for favorite numbers that contain decimal points, for all the math geeks of the world.

int main (int argc, const char * argv[]) {
	//Computes our favorite number
	float divideBy = 3.0;
	float favoriteNumber = (3.14159265 * integerForSeedValue(8)) / divideBy;
	favoriteNumber--;

	/* now let’s tell the world
		what our favorite number is! */
	printf("My favorite number is %f", favoriteNumber);
	return 0;
} 

None of this should be at all surprising. We can do math, transfer the number around, use the decrement operator to count down and lots of other operations that are totally normal. But there are a few things to note that might not seem obvious at first.

The first is that we can mix and match integers and floats in our code. We see this in the line where favoriteNumber is declared; we multiply a float by an integer, which is being returned by our function.

Secondly, note that we can use a float to store an integer, when we store 3.0 into the divideBy variable. This makes perfect sense; an int is just a float with the decimal point pushed to the side, with nothing to the right of it.

Last, note that we use a new conversion specification to print out our float. Naturally enough, floats are represented with an ‘f’ character.

This code should compile and run fine. If you’re on the bleeding edge and running Panther and Xcode, you should get a nice window that opens to display the output. If you’re in Project Builder on Jaguar, the output should appear in the Run tab.

Now let’s regress a little and explore some new territory. I’ve put a few twists on things:

int main (int argc, const char * argv[]) {
	//Computes our favorite number
	int favoriteNumber = (3.14159265 * 4.321) / 2.92;
	
	/* now let's tell the world
		what our favorite number is! */
	countTo(favoriteNumber);
	
	return 0;
}

Related Reading

Learning Cocoa with Objective-C
By James Duncan Davidson, Apple Computer, Inc.

Note that favoriteNumber is back to storing an int on the heap. When we assign a value to favoriteNumber, we assign using only floats that we have written into our code. So what does favoriteNumber actually contain?

You might guess that it contains 4.64891159, which is what Google’s Calculator tells us it should equal. But we know that an int can’t store that kind of detail; it is limited to doing only whole numbers. Indeed, if we take a look, favoriteNumber contains the simple value 4. It has taken the float we gave it and done an automatic conversion into what it understands, by simply truncating the number to an int, throwing away the decimal places. Note that it didn’t round; 4.99999 will still be truncated to 4.

You might ask why we ever use int. After all, it drops all this information we gave it that might be useful. Those numbers after the decimal could make a difference. The reason here is that we’re probably using the wrong type; if we cared, we’d use a float like we do in the first snippet in this lesson. However, there are plenty of times when you simply don’t care about those extra decimal places, and this is one of those times; our countTo() function takes an integer, not a float; it won’t count to 4.6; it will only count to 4. Since we don’t need the extra data, we ignore it.

One last word on float and then we’ll move on. Computers find it very easy to count in whole numbers, just like you and I do. They’re simple. Decimals are harder, because you never know how to split up the space. There are very complex rules for doing so, but even so float will sometimes play little games with you, and you’ll find out that the variable you set to 4.0 is now 3.9999998. That’s because float stores not the data you give it, but a close approximation of that data. It’s close enough for most needs, and if you need more precision there are other data types that provide them. What this means for you is that you can’t check to see if floats equal each other, because they sometimes won’t even when they should. Instead, you’ll find yourself using code like this:

if (fabs(varISetToFourPointOh - 4.0) > .01)
//close enough for government work!

We’re using the fabs function, which computes the absolute value of a float. Floats really aren’t that hard once we understand these limitations, but there’s another type of data we use a lot that we don’t know how to store yet.

Strings

Probably the most obvious thing we don’t know how to store yet is text. We can hardcode text into our programs, but we can’t store it, and we certainly can’t manipulate it. To do that, we need a few new types.

The first one we’ll learn is called char, and it stores a single character of text. It does this through a little bit of clever trickery called encoding. Basically, the idea is the same as the old decoder cards that used to come in Cracker Jacks: each letter is given an appropriate number, and you can just look at the numbers and convert them into characters as you go. So the string ‘COCOA’ becomes the sequence of numbers 67, 79, 67, 79, 65 (Yes, the letter ‘A’ is the number 65. Why? Because that’s how it is). You can find a complete listing of all the conversions here.

It’s easy to understand how this fits into what we know about C so far; the variable stores this new type in the same 32 bits, and now we just read it as a letter instead of as a number. Simple enough.

The trick comes when we have to represent a sequence of numbers. How do we do that? Well, there’s the hard way (note the %c is the conversion specification for a _c_haracter).

int main (int argc, const char * argv[]) {
	//print some letters the hard way
	char letterC = 'c';
	char letterO = 'o';
	char letterA = 'a';
	
	printf("%c%c%c%c%c", letterC, letterO, letterC, letterO, letterA);
	return 0;
}

But this is obviously ridiculous. There’s a much better way, and it involves some of the magic we learned last week, together with a concept that’s new this week:

int main (int argc, const char * argv[]) {
	//print some letters the easy way
	char* string = "cocoa";
	
	printf("%s", string);	
	//yes, we could just put them in the format string. But that's no fun.
	return 0;
}

Quite a bit better. But what’s going on here? Well, we’ve just made a string and stored it into a variable, appropriately enough called string. This variable, as you can see, is referenced by a pointer. But it’s a pointer to a char, not a new type. How does that work?

A Vast Array of Possibilities

It works because programs deal with strings all the time, and C gives us a little shortcut to working with them. Instead of defining every character we print, we can define an array of them, and use them all together as a group. So what in the world is an array?

An array is not a new data type; you can use it with any data type. With strings, we’re making an array of characters. But you can just as easily make an array of ints or floats or pointers or whatever. But basically, an array is a list of variables, all the same type, which you want to work with as a group. Let’s look at some code to see an example:

int main (int argc, const char * argv[]) {
	int numbers[5];		//define an array
	numbers[0] = 67;	//put some data in
	numbers[1] = 79;
	numbers[2] = 67;
	numbers[3] = 79;
	numbers[4] = 65;
	
	//show the data off
	printf("My favorite numbers are %d, %d, %d, %d, %d",
		numbers[0],	//get some data out
		numbers[1],
		numbers[2],
		numbers[3],
		numbers[4]);
	return 0;
}

Here we see pretty much everything we need to know about using arrays. We can define one by putting a number in square brackets after a variable name. This number is the capacity of the array; it defines how many elements it can hold. In doing this, we have made an array with that name, with room for the specified number of elements, and each element in the array is of the specified type. Note that the capacity must be a constant; it cannot be a variable. This is because the compiler allocates memory for the arrays; they are just normal variables in the run time.

From now on, we can look at the whole array by referring to it by name, in this case numbers. But if we want to use one of the elements of the array, we must refer to it by index. Indexes specify position by their distance after the start, so the start is always 0 (being 0 places after itself) and the last index is the one less than capacity we specified when we created the array. Note that when specifying indexes we are free to use a variable if we want. The index is placed inside square brackets and put after the array name; so numbers[2] refers to the element at index 2 (the third element) in the array numbers.

If we want to assign a value to an element, we can put data into it just like we do a normal variable, with the assignment operator, but we must use an index to make sure we’re assigning the value to one of the array’s elements, not the array itself. Likewise, we can get the data out of the array by just appending the brackets and an appropriate index.

It is important to note that in C, unlike in many more modern languages like Java and Python, arrays have no idea how many elements they contain; you must keep track of that and make sure that you don’t try to put data into elements that don’t exist (This is how buffer overruns – a too-common security hole – work).

Pages: 1, 2

Next Pagearrow