Objective-C pointers are making me bonkers!
August 1, 2010 1:51 PM   Subscribe

I am trying to learn Objective-C. I'm a total n00b and am having a hard time with pointer syntax. Please help me decipher this code.

I understand that &variable denotes the memory address of a variable. What I'm confused about is how does *originalValue change the data of radius? Does the * mean it points to the address space or the value?

To answer, can you trace with comments how this OriginalValue not only gets access to radius' memory address but also changes its value?

Thanks so much.

#import "NotifyingClass.h"
#import "MathUtilities.h"

@implementation NotifyingClass

- (IBAction)displaySomeText : (id)sender;

{
float radius = 0.0;
float circumference = [self generateValue: &radius];
//memory address of radius variable sent to generateValue method,
[textView insertText: [NSString
stringWithFormat: @"With a radius of %f, the circumference is: %f\n",
radius, circumference]];

}

- (float)generateValue:(float *)originalValue
{
float radius = [textField floatValue];
//what's entered in the text field becomes the int variable radius.
*originalValue = radius;

float circumference = [MathUtilities circumferenceFromRadius: radius];

return circumference;
}

@end
posted by captainscared to Computers & Internet (16 answers total) 3 users marked this as a favorite
 
The * means it's pointing to the value. So we are passing a memory address (pointer) to generateCircumference() which then dereferences it using * so that you modify the value at that address rather than editing the address itself.
posted by Allenthar at 2:14 PM on August 1, 2010


This is a C question, really. Objective C doesn't have anything to do with it. You should google basic C pointer syntax and tutorials. I could write an explanation here but frankly I'm not very good at that and I would just confuse you. Just look for basic C pointer tutorials.
posted by chairface at 2:20 PM on August 1, 2010


Best answer: Does the * mean it points to the address space or the value?

"*foo" means "the thing pointed to by foo". If you assign to *foo then it means to take the value stored in foo and treat it as an address and store the result of the assignment at that address. If you're familiar with Excel, this is INDIRECT().

And yeah, get a good book/tutorial on C as learning this is a major requirement to doing anything useful in C.
posted by Rhomboid at 4:26 PM on August 1, 2010 [1 favorite]


Oh an as to your question: how this OriginalValue not only gets access to radius' memory address but also changes its value

I think you might be trying to conceptualize this at too high an abstraction level -- at the CPU level all a program does is load and store values at memory addresses. Other than that provided by the operating system (i.e. you can't clobber other programs' memory spaces or the memory space of the operating system), there is really no such thing as access control: you instruct the CPU to load or store a value at address X and it does. At this level the entirety of memory is just a big long field of addresses with no structure; any organization that exists in the program, such as declaring "struct foo has members bar and baz" exists only at the language level, but at the actual physical level of what the CPU sees, there's only a long series of bytes. The compiler turns a construct like "foo.baz" into the instructions to get the address of foo, add to that offset of baz within struct foo, and then fetch the value at that location. But that is still just "get value at address X", and if you knew X you could get that same value without even knowing what foo was or that it had a member baz.

So to answer your question, it gets access to radius' memory because by using pointers you are mimicking the low-level reality of what happens in a CPU: any address can be read or written at any time. It changes its value because it's been passed the location where that value is stored.

This is, by the way, one of the reasons that C is often described as dangerous: you have full access to the low level operations of the CPU, which means you can cause grave harm if you're not careful. Languages that are sometimes described as "higher level" than C such as python, java, perl, PHP, etc. all strive to eliminate such abilities and abstract them away so that you can't directly access memory. They do this so that the language interpreter/compiler can enforce access controls that make it hard to corrupt data by writing the wrong thing to the wrong place or by accessing members that are supposed to be private.
posted by Rhomboid at 4:48 PM on August 1, 2010


Best answer: Dramatis Personae:
generateValue - a method/member function of the class
originalValue - a float pointer -- type is *float
radius - a float -- type is float
&radius - a float address -- type is *float

We want generateValue to change the value inside radius. The problem is that C (and Obj-C) are what's called pass-by-value -- if I have f(x) then f() only gets to know what x's value is, rather than being able to manipulate x.

So in C, if we want a function to manipulate an actual object in memory, we pass the address to the function. That's why we pass &radius (which is referenced inside the function as the pointer originalValue) instead of passing radius.

So inside the member function, originalValue is an object in memory whose VALUE is the address in memory of the object radius. If we say originalValue = somenumber; then it is clear to see that the VALUE of originalValue -- which is to say, the memory address it points to -- will change. It will then no longer point to radius.

So how do we manipulate the value of radius? By "dereferencing" originalValue. The * character, in addition to meaning "hey this is a pointer type" also means "hey we're talking about manipulating the thing we're pointing at, not the pointer itself."

So *originalValue = radius just takes the local value of radius and assigns it to the radius object that was passed to the method.

Sorry if that's confusing. The author of the code above is kind of crappy about naming conventions. Here's a simpler example:

void doublethisint(int *n) {
/* n is the pointer to our integer */
/* *n is the integer itself - we don't know it's name, but we can access it directly through *n */
*n = (2 * (*n));
}

Hope this helps!
posted by thesmophoron at 5:40 PM on August 1, 2010 [1 favorite]


I meant to also note that there is such a thing as access control as you can map pages read-only and so on, but that's not remotely the same thing as the language-level access control (i.e. private members) and it would only confuse the issue to try to bring that up.
posted by Rhomboid at 5:49 PM on August 1, 2010


Response by poster: Thanks everyone. Very helpful. FWIW, I'm reading Tim Isted's Beginning Mac Programming.
posted by captainscared at 6:20 PM on August 1, 2010


I recommend that you read Kernighan and Ritchie's The C Programming Language. Its explanation of pointers, arrays, structures, and their interactions is clear and complete. Objective-C is a strict superset of plain C, so anything you learn will be applicable. The book is very well written, and worth reading even though it is pretty old.
posted by scose at 6:51 PM on August 1, 2010 [2 favorites]


Here's my stab at explaining C pointers:
01  float radius = 0.0;
02  float circumference = [self generateValue: &radius];
01 declares a new float variable, that the compiler will mark off a location in memory for (and assign an initial value of 0.0). References to 'radius' will refer to the float value at this location while references to '&radius' will refer to the literal location value (or memory address).

02 declares another float variable and assigns it some computed value. The key is that the call to generateValue passes the memory address of radius and not the value of radius.
01  - (float)generateValue:(float *)originalValue
02  {
03    float radius = [textField floatValue];
04    *originalValue = radius;
05    float circumference = [MathUtilities circumferenceFromRadius: radius];
06    return circumference;
07  }
The function definition of generateValue says it takes one (float *)originalValue, which basically means an argument that it will interpret as an address to a data type of float. Or in other words, the function is given the raw address of some memory location that was declared and initialised outside of it, and it should just assume that there's a float data type at that location.

In 03, 'originalValue' by itself would just refer to a variable who's current value is the memory address that was passed in. '*originalValue' instead will refer to the value stored at that location, (i.e. the value of radius). The key here is that the location you're writing is the same location that radius above refers to, since you just literally passed in its address.

This could've been done without pointers but I guess the example was to show that you can call other functions and have them change variable values without using return values by just passing around literal memory locations (pointers).

For what's it worth, C pointers only made sense to me after I learned assembly.
posted by tksh at 6:58 PM on August 1, 2010


Oh man, should've previewed. But yeah, I second everyone's advice to get familiar with basic C programming first.
posted by tksh at 7:00 PM on August 1, 2010


Pretend that all of memory is a giant array. Which it kind of is. Usually we talk about variables, which are particular storage spots in memory. But pointers store indices into the big array we call "memory".

You can think of pointers as indices into the giant array called "memory", and you won't be too wrong.
posted by pmb at 9:34 PM on August 1, 2010


pmb makes a good point.

The point of pointers is to be able to refer to data in a smaller way, without the messiness of global variables. If you have a giant array and you need to pass it to a function, instead of passing the whole array, you just pass the pointer to the array.

This made C a particularly fast and functional language for people who were used to assembler and machine languages. The compiler and the executable doesn't have to spend any time figuring out how to pass data back and forth. However, with modern machines, this is less necessary for most applications.

It is particularly good for sorting- the speed of the sort no longer depended on the size of the data in each record, because you aren't passing the data around. You are just passing the memory location of the data.

I'm not sure if this is how it is done anymore, but back in the day of graphics, you would use a pointer-like mechanism to put stuff on the screen. The application would "build" the bitmap in the background, and then send a "pointer" to the display driver to the location of that bitmap for it to use on its next refresh. Made graphics seem instant.

Another thing to keep in mind is the opposite of * which is &. If you have a variable foo, &foo will give you its pointer.

What makes pointers "dangerous" is that if you screw up and say add one pointer to another, you may get a valid memory location. That is some place that's not good to be messing with. I think modern compilers will fail out or fix this. It is also "dangerous" because you aren't working with copies of the data- if you send "foo" to a function, it makes a copy of foo for the function to manipulate, which it then returns. If you send &foo, the function will be working with the actual data.
posted by gjc at 6:24 AM on August 2, 2010


Response by poster: I understand that *var1 =&var2 (var1 is pointing to the address space of var2.) But when you dereference *var1 = 42, how is var1 still referencing var2, when you have reassigned it's value?

If that makes sense. Seems like var 1 now has a value AND an address space.

Thanks.
posted by captainscared at 9:41 AM on August 2, 2010


You would never write "*var1 = &var2" unless var1 was a pointer to a pointer, i.e. type (int **). You only use the * in front of the variable name when you want to dereference the pointer (access the things it points to), but to change what the pointer points to you assign to it normally.

var1 = &var2; /* var1 now points to var2 */
*var1 = 42; /* the thing var1 points to now holds 42, but var1 itself is unchanged and still points to whatever it was pointing to before */
posted by Rhomboid at 4:16 PM on August 2, 2010


Best answer: Oh, I see how you might be getting confused now because there's a difference between declaring a variable and assigning to it, and you can combine the two:

To go with my last example, var1 and var2 would have had to have been declared as:

int var2; /* int */
int *var1; /* pointer to int */

The "int *" part in the declaration is the type, as you have to tell the compiler what type var1 has, but once the variables have been declared that is not necessary as the compiler already knows the type, so assigning to var1 becomes just

var1 = &var2;

You can combine declaration with assignment-of-an-initial-value, which means you can have:

int *var1 = &var2;

So the confusion here is that you've got something that looks like "*var1 = &var2" but the * here does not mean dereference var1, it is part of the type specifier of the declaration. That means that these two statements have very different meanings even though they both say "*var1":

int *var1 = &var2; /* declare var1 as a pointer to int and assign it the initial value of the address of var2, i.e. make it point to var2 */
*var1 = 42; /* dereference var1 and store 42 in what var1 points to, leaving var1 itself unchanged */

It's important to be able to differentiate between an declaration and a regular statement.
posted by Rhomboid at 4:26 PM on August 2, 2010


By the way, type specifiers have a lot of flexibility to their grammar, so for example you could declare pointers as
int    var1 = 42;
int*   var2 = &var1;
Here "int*" is the type specifier, which is a bit more orthogonal since it makes it more clear the grouping between the type specifier and the assignment, and "*var2" can be reserved to always mean dereferencing. This is one of those personal style decisions, so sometimes you'll see code like this and other times you'll see it as above, so unfortunately you have to be able to deal with both.
posted by Rhomboid at 5:07 PM on August 2, 2010 [1 favorite]


« Older How do I teach myself to brush my teeth?   |   Will helping him raise his credit score lower my... Newer »
This thread is closed to new comments.