Wednesday, December 4, 2013

Pointer Parameters and the Address Operator


Objective-C is chock-a-block with pointers (and asterisks), because that’s how Objective-C refers to an object. Objective-C methods typically work with objects, so they typically expect pointer parameters and return a pointer value. But this doesn’t make things more complicated. Pointers are what Objective-C expects, but pointers are also what Objective-C gives you. Pointers are exactly what you’ve got, so there’s no problem.

For example, one way to concatenate two NSStrings is to call the NSString method stringByAppendingString:, which the documentation tells you is declared as follows:
    - (NSString *)stringByAppendingString:(NSString *)aString

This declaration is telling you (after you allow for the Objective-C syntax) that this method expects one NSString* parameter and returns an NSString*. That sounds messy, but it isn’t, because every NSString is really an NSString*. So nothing could be simpler than to obtain a new NSString consisting of two concatenated NSStrings:
    NSString* s1 = @"Hello, ";
    NSString* s2 = @"World!";
    NSString* s3 = [s1 stringByAppendingString: s2];

Sometimes, however, a function or method expects as a parameter a pointer to a thing, but what you’ve got is not that pointer but the thing itself. Thus, you need a way to create a pointer to that thing. The solution is the address operator (K&R 5.1), which is an ampersand before the name of the thing.
For example, there’s an NSString method for reading from a file into an NSString, which is declared like this:
    + (id)stringWithContentsOfFile:(NSString *)path
                          encoding:(NSStringEncoding)enc
                             error:(NSError **)error	
Never mind for now what an id is, and don’t worry about the Objective-C method declaration syntax. Just consider the types of the parameters. The first one is an NSString*; that’s no problem, as every reference to an NSString is actually a pointer to an NSString. An NSStringEncoding turns out to be merely an alias to a primitive data type, an NSUInteger, so that’s no problem either. But what on earth is an NSError**?
By all logic, it looks like an NSError** should be a pointer to a pointer to an NSError. And that’s exactly what it is. This method is asking to be passed a pointer to a pointer to an NSError. Well, it’s easy to declare a pointer to an NSError:
    NSError* err;
But how can we obtain a pointer to that? With the address operator! So our code might look, schematically, like this:
    NSString* path = // something or other
    NSStringEncoding enc = // something or other
    NSError* err = nil;
    NSString* result =[NSString stringWithContentsOfFile: path encoding: enc error: &err];
The important thing to notice is the ampersand. Because err is a pointer to an NSError, &err is a pointer to a pointer to an NSError, which is just what we’re expected to provide. Thus, everything goes swimmingly.
You can use the address operator to create a pointer to any named variable. A C function is technically a kind of named variable, so you can even create a pointer to a function! This is an example of when you’d use the name of the function without the parentheses: you aren’t calling the function, you’re talking about it. For example, &square is a pointer to the square function. Moreover, just as the bare name of an array is implicitly a pointer to its first element, the bare name of a function is implicitly a pointer to the function; the address operator is optional.

No comments:

Post a Comment