pointer to int conversion compiler trouble? - objective-c

So I'm using Xcode to write a few tiny Objective-C apps and I have the line of code:
int * foo;
foo = 5;
NSLog(#"does it work... %i", foo);
Now it compiles and runs just fine, but I was wondering, why does it give me the warning, "Incompatible integer to pointer assigning..."? I thought the code above tells it to set whatever foo is pointing to to 5, not to set the pointer itself to 5. Can anyone help me out with this?

This piece of code works by accident: the int pointer happens to have sufficient space to hold an integer value, and NSLog reinterprets the pointer as an integer, but the program is still incorrect.
A pointer to int should be assigned an address of an integer variable, like this:
int *foo;
int var;
foo = &var;
Now you can assign the variable through the pointer:
*foo = 5;
You can also read the value through the pointer or through the variable:
NSLog(#"%d %d", *foo, var);

Related

Why does passing an unsigned int to performSelector lose bits?

I am trying to pass a hex value as an unsigned int to a method using a dynamic link. The value I pass as a parameter is getting corrupted somehow. What is happening?
- (void)callPerformSelector
{
NSNumber *argument = [NSNumber numberWithUnsignedInt:(unsigned int)0xFFFFFFFF];
SEL selector = NSSelectorFromString(#"testPerformSelector:");
NSLog(#"testPerformSelector object %#", argument);
[self performSelector:selector withObject:argument];
}
- (void)testPerformSelector:(unsigned int) arg1
{
NSLog(#"testPerformSelector unsigned int %u", arg1);
NSLog(#"testPerformSelector hex %X", arg1);
}
Output is:
testPerformSelector object 4294967295
testPerformSelector unsigned int 4294967283
testPerformSelector hex FFFFFFF3
Because it should be:
- (void)callPerformSelector
{
NSNumber *argument = #0xFFFFFFFF;
SEL selector = #selector(testPerformSelector:);
NSLog(#"testPerformSelector object %#", argument);
[self performSelector:selector withObject:argument];
}
- (void)testPerformSelector:(NSNumber *) arg1
{
NSLog(#"testPerformSelector unsigned int %u", arg1.unsignedIntValue);
}
unsigned int and NSNumber * is two different things
There's an easy reason and a complex reason.
Simple reason: Why this doesn't work. The first argument to the target of a performSelectorWithObject must be an object. You are specifying a pointer to an unsigned integer in your function signature but then passing an object (NSNumber) when you call it. So instead of:
- (void)testPerformSelector:(unsigned int) arg1
you should have
- (void)testPerformSelector:(NSNumber *) arg1
You will then need to use NSNumber's unsignedIntValue to get the 0xFFFFFFFF out of the object.
The complex reason is much more interesting: Why this nearly works and looks like it loses a few bits. NSNumber is an object that wraps a numeric value this is very different from a raw numeric value. However NSNumber is implemented as a tagged pointer so although objective-c knows it is an object and treats it like an object, a subset of NSNumber values are implemented as tagged pointers where real values are coded into the "pointer" and the fact that this is not a normal pointer is indicated in the (otherwise always zero) bottom four bits of a pointer see Friday Q&A.
You can only pass objects to selectors and not primitive types, and therefore the selector should be:
- (void)testPerformSelector:(NSNumber *) arg1
{
NSLog(#"testPerformSelector hex %x", [arg1 unsignedIntValue]);
}
Update: As pointed-out by #gnasher729, the reason the number passed appears to be -13 is because it's a tagged pointer.

Need to use BOOL* instead of BOOL

I am making a chess game in Objective-C. In this game I need to use BOOL* instead of BOOL because I need a pointer to a boolean variable. When I try to use BOOL*, it gives me a warning when I try to do this:
BOOL *isWhiteTurn;
isWhiteTurn = YES;
The warning is:
Incompatible integer to pointer conversion assigning to 'BOOL *' (aka
'signed char *') from 'BOOL' (aka 'signed char')
A pointer is exactly what is sounds like, it points to some other memory.
Lets take this simple example:
BOOL actualVariable = FALSE;
BOOL *pointerVariable = &actualVariable;
That makes pointerVariable point to actualVariable. Using the derefernece operator (unary *) you can get the value of what a pointer points to:
printf("Value of *pointerVariable = %d\n", *pointerVariable);
That should print
Value of *pointerVariable = 0
More graphically you can look at it this way:
+-----------------+ +----------------+
| pointerVariable | ----> | actualVariable |
+-----------------+ +----------------+
You can also use the dereference operator to change the value of where the pointer points:
*pointerVariable = TRUE;
If you declare a pointer, and don't make it point anywhere, then attempting to dereference the pointer (i.e. get what the pointer points to) will result in undefined behavior.
Now regarding your warning. A pointer variable is actually a simple integer, whose value is the address of where it points. That means you can in theory assign any integer value to it, and the program will think that the value is an address of something valid. Most of the time it is not something valid though.
You get the warning because usually using an integer value to initialize a pointer is the wrong thing to do, you should initialize the pointer with another pointer to the same type.
As it became apparent in the comments, you have some function
taking a BOOL * parameter, for example
void foo(BOOL *boolPtr) {
*boolPtr = NO;
}
and you need to pass the address of your BOOL variable to
that function:
BOOL isWhiteTurn = YES;
foo(&isWhiteTurn);
// Now isWhiteTurn == NO

How do I use the assignment operator *= to multiply a value by ten?

Quite simply:
current *= 10;
Returns an "invalid operands to binary *" build error.
int *current = 0;
- (void)bOnePress:(id)sender {
current *= 10;
current += 1;
[resultDisp setText:[NSString stringWithFormat: #"%i", current]];
}
Google won't tell me what I'm doing wrong, so I'm asking here. :S
Edit: ANSWERED:
int current = 0;
Remove the *.
You have declared your variable as a pointer to memory address 0, not as an integer variable that can be multiplied.
You declared a pointer to an integer, not an integer. You probably want something like
int current = 0;
Don't get confused with Objective-C manner to deal with objects. These are always pointers, but an int is just an int, it's a type by itself. You will only use a pointer to an int when you want to reference another portion in memory that contains an int.
You're multiplying the pointer by ten, not the value.
it should read:
*current *=10;
*current *= 10;

Why don't I declare NSInteger with a *

I'm trying my hand at the iPhone course from Stanford on iTunes U and I'm a bit confused about pointers. In the first assignment, I tried doing something like this
NSString *processName = [[NSProcessInfo processInfo] processName];
NSInteger *processID = [[NSProcessInfo processInfo] processIdentifier];
Which generated an error, after tinkeing around blindly, I discovered that it was the * in the NSInteger line that was causing the problem.
So I obviously don't understand what's happening. I'll explain how I think it works and perhaps someone would be kind enough to point out the flaw.
Unlike in web development, I now need
to worry about memory, well, more so than in web development. So when I
create a variable, it gets allocated a
bit of memory somewhere (RAM I
assume). Instead of passing the
variable around, I pass a pointer to
that bit of memory around. And
pointers are declared by prefixing the
variable name with *.
Assuming I'm right, what puzzles me is why don't I need to do that for NSInteger?
NSInteger is a primitive type, which means it can be stored locally on the stack. You don't need to use a pointer to access it, but you can if you want to. The line:
NSInteger *processID = [[NSProcessInfo processInfo] processIdentifier];
returns an actual variable, not its address. To fix this, you need to remove the *:
NSInteger processID = [[NSProcessInfo processInfo] processIdentifier];
You can have a pointer to an NSInteger if you really want one:
NSInteger *pointerToProcessID = &processID;
The ampersand is the address of operator. It sets the pointer to the NSInteger equal to the address of the variable in memory, rather than to the integer in the variable.
The reason that you don't declare NSInteger with a * is because it isn't an object. An NSInteger is simply an int or a long:
#if __LP64__
typedef long NSInteger;
#else
typedef int NSInteger;
endif
If it's being used in a 32-bit application, it's a 32-bit integer, and if it's being built in a 64-bit application, it's a 64-bit integer.
Of course, you can pass an NSInteger as a pointer, but most functions simply take arguments as an NSInteger and not a pointer to it.
Objects, on the other hand, can only be passed to other functions as pointers. This is because objects have memory dynamically allocated for them, and so cannot be declared on the stack. Since an int or long has a fixed amount of memory allocated for them, this is not an issue.
The * means “pointer”. The object variable holds a pointer to an object, so it has a *; the NSInteger variable holds an NSInteger, not a pointer to an NSInteger, so it does not have a *. Putting the * on that variable gives you at least a warning because you're putting an integer into a pointer variable.
NSInteger is just a typedef for int, AFAIK.
Working with pointers
NSInteger integer1 = 1;
NSLog(#"1. integer1:%ld &integer1:%p", integer1, &integer1);
//1. integer1:1 &integer1:0x7ffee59e8a98
NSInteger *integer2 = &integer1;
NSLog(#"2. integer2:%p &integer2:%p *integer2:%ld", integer2, &integer2, *integer2);
//2. integer2:0x7ffee59e8a98 &integer2:0x7ffee59e8a90 *integer2:1
*integer2 = 2;
NSLog(#"3. integer2:%p &integer2:%p *integer2:%ld \t integer1:%ld &integer1:%p", integer2, &integer2, *integer2, integer1, &integer1);
//3. integer2:0x7ffee59e8a98 &integer2:0x7ffee59e8a90 *integer2:2 integer1:2 &integer1:0x7ffee59e8a98

object_getInstanceVariable works for float, int, bool, but not for double?

I've got object_getInstanceVariable to work as here however it seems to only work for floats, bools and ints not doubles. I do suspect I'm doing something wrong but I've been going in circles with this.
float myFloatValue;
float someFloat = 2.123f;
object_getInstanceVariable(self, "someFloat", (void*)&myFloatValue);
works, and myFloatValue = 2.123
but when I try
double myDoubleValue;
double someDouble = 2.123f;
object_getInstanceVariable(self, "someDouble", (void*)&myDoubleValue);
I get myDoubleValue = 0. If I try to set myDoubleValue before the function eg. double myDoubleValue = 1.2f, the value is unchanged when I read it after the object_getInstanceVariable call. Setting myIntValue to some other value before the getinstancevar function above returns 2 as it should, ie. it has been changed.
then I tried
Ivar tmpIvar = object_getInstanceVariable(self, "someDouble", (void*)&myDoubleValue);
If I do ivar_getName(tmpIvar) I get "someDouble", but myDoubuleValue = 0 still! Then I try ivar_getTypeEncoding(tmpIvar) and I get "d" as it should be.
So to summarize, if typeEncoding = float, it works, if it is a double, the result is not set but it correctly reads the variable and the return value (Ivar) is also correct.
I must be doing something basic wrong that I cant see so I'd appreciate if someone could point it out.
object_getInstanceVariable is a confused little function. It is documented that the last parameter is a void ** parameter—that is, you pass the address of a void * variable and get a pointer to the instance variable—but it is implemented as if it was a void * parameter—that is, you pass the address of the variable that you want to hold a copy of the instance variable. The problem is that the implementation ignores the size of the instance variable and just does a pointer copy. So anything that's the same size as a pointer will work perfectly. If you're running on a 32-bit architecture, only the high 32 bits will be copied. (You should witness the same behavior with a long long instance variable as well.)
The solution is to use the primary API, key-value coding, using -valueForKey:.
The other solution: If you wanted to write a fixed version, say as a category for NSObject, it would look something like this:
#implementation NSObject (InstanceVariableForKey)
- (void *)instanceVariableForKey:(NSString *)aKey {
if (aKey) {
Ivar ivar = object_getInstanceVariable(self, [aKey UTF8String], NULL);
if (ivar) {
return (void *)((char *)self + ivar_getOffset(ivar));
}
}
return NULL;
}
#end
Then your code would look like this:
double myDoubleValue = *(double *)[self instanceVariableForKey:#"someDouble"];
What about using valueForKey:?
NSNumber * value = [self valueForKey:[NSString stringWithUTF8String:ivar_getName(tmpIvar)]];
NSLog(#"Double value: %f", [value doubleValue];
Note: this requires you to have a "someFloat" method. If you want to use setValue:forKey:, you'll also need the "setSomeFloat:" method. This is easily implemented by declaring the ivar as an #property and synthesizing it.