Assignment problem

Discussion in 'Mac Programming' started by mdeh, Jan 28, 2009.

1. mdeh Expand Collapse macrumors 6502

Joined:
Jan 3, 2009
#1
I get a warning which I cannot understand.

Given Fraction is a class, defined in "Fraction.h",

Code:
```//
//  Fraction.h
//  Chap 7 Prgm 7.1

//

#import <Foundation/Foundation.h>

@interface Fraction : NSObject {
int numerator;
int denominator;
}

@property int numerator, denominator;

/** class initialization **/

- initWith: (int)  n over: (int) d;

-(void) reduce;

/* modified accessor*/
-(void) setTo: (int) n over: (int) d;

/*utilities*/

-(double) convertToNum;

-(void) print;

@end
```
Code:
```//
//  Fraction.m
//  Chap 7 Prgm 7.1

//

#import "Fraction.h"
#import "Comparison.h"

@implementation Fraction;

@synthesize denominator, numerator;

- initWith: (int)  n over: (int) d
{
if ( ! ( self = [super init]))
return nil;

if (self)
[self setTo: n over: d];

return self;

}

-(double) convertToNum
{
if (denominator != 0)
return (double) numerator / denominator;
else
return 1.0;
}

/*********SPECIFIC TO EXERCISE 7-5 ***************

-(void) print
{
float f = 0.00;

if ( (f = (float) numerator / denominator) > 1)

NSLog(@"%i %i/%i", numerator/denominator, numerator % denominator, denominator);

else

NSLog(@"%i/%i ", numerator, denominator);
}

-(void) setTo: (int) n over: (int) d
{
numerator = n;
denominator = d;
}

/*
-(Fraction *) add: (Fraction *) f
{
int resultNum, resultDenom;
Fraction* result = [[Fraction alloc] init];

resultNum = numerator * f.denominator + denominator * f.numerator;
resultDenom = denominator * f.denominator;

[result setTo: resultNum over: resultDenom];
[result reduce];
return result;
}

*/

-(void) reduce
{
int u, v, temp;
BOOL isNegativeNumerator = NO;
BOOL isNegativeDenominator = NO;

if ( numerator < 0)
{
isNegativeNumerator = YES;
numerator = -numerator;
}

if ( denominator < 0)
{
isNegativeDenominator = YES;
denominator = -denominator;
}

u = numerator;
v = denominator;

while (v != 0) {
temp = u % v;
u = v;
v = temp;
}

numerator /= u;
denominator /= u;

if ( isNegativeNumerator == YES || isNegativeDenominator == YES)
numerator = -numerator;

}

@end

```
Code:
```#import "Fraction.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

Fraction *fractArr[10], *fractPtr;
fractPtr = fractArr;  /*warning: assignment from incompatible pointer type */

Fraction *aFrac = [ [ Fraction alloc] initWith: 3 over: 4];
Fraction *bFrac = [ [ Fraction alloc] initWith: 7 over: 8];

fractArr[0] = aFrac;
fractArr[1] = bFrac;

[fractPtr print];
fractPtr++;
[fractPtr print];

[aFrac release];
[bFrac release];
[pool drain];
return 0;
}
```

Help would be appreciated.
Thanks

2. North Bronson Expand Collapse macrumors 6502

Joined:
Oct 31, 2007
Location:
San José
#2
Have you tried making sure that your initWith: returns a Fraction?

-(Fraction *) initWith: (int) n over: (int) d;

3. mdeh Expand Collapse thread starter macrumors 6502

Joined:
Jan 3, 2009
#3

I don't think that is the problem..but thanks. The issue has to do with assignment of a ptr to an array...I think.

4. kpua Expand Collapse macrumors 6502

Joined:
Jul 25, 2006
#4
fractArr is a Fraction ** since it is a pointer to an array.

fractPtr, is obviously only a Fraction *, hence the warning about incompatible types.

5. lee1210 Expand Collapse macrumors 68040

Joined:
Jan 10, 2005
Location:
Dallas, TX
#5
Bah... by the time i got this compiling in a Obj-C 1.0 environment i was beat to the punch. Fraction ** is the right type for fractPtr... you'll have to derference fracPtr before passing a message to the Fraction * it's pointing to.

-Lee

6. mdeh Expand Collapse thread starter macrumors 6502

Joined:
Jan 3, 2009
#6
Yes...I thought it was something like that.
So, this is what I am ****trying*** to do.
Declare an array that will hold Fraction objects. ??? Fraction *arr[IntConst];

Declare a ptr of type Fraction ??? *myFractPtr;

So, how do I then assign a ptr to point at the array that holds these objects. But, having said that, further in Kochan's text, he gives the example that once one has declared and assigned appropriately, then one could use the pointer to do something like this ( given print is a message understood by Fraction class) [myFractPtr print]...which is also a little confusing, as it implies that myFractPtr is de-referenced by the [ ] operator....hence all the confusion.

7. kpua Expand Collapse macrumors 6502

Joined:
Jul 25, 2006
#7
Why don't use just use NSArray?

Anywho...

Code:
```Fraction *fracArr[10];
Fraction **fracPtr = fracArr;

// Fill array
*fracPtr = [[Fraction alloc] init...];

[(*fracPtr) someMethod];
```

8. mdeh Expand Collapse thread starter macrumors 6502

Joined:
Jan 3, 2009
#8
Lee, you are correct...as always!!!

I just got it to work, but not quite sure why?

Code:
```Fraction *fractArr[10], **fractPtr;
fractPtr = fractArr;
Fraction *aFrac = [ [ Fraction alloc] initWith: 3 over: 4];
Fraction *bFrac = [ [ Fraction alloc] initWith: 7 over: 8];

fractArr[0] = aFrac;
fractArr[1] = bFrac;

[*fractPtr print];
fractPtr++;
[*fractPtr print];```

This does not match Kochan's example, so not sure if I am correct or not?

9. lee1210 Expand Collapse macrumors 68040

Joined:
Jan 10, 2005
Location:
Dallas, TX
#9
Steve might need to chime in, then. i don't know how else to have this work without multiple assignments from the array, or indexing the array when passing the message, etc. And those certainly will not work using ++.

-Lee

10. mdeh Expand Collapse thread starter macrumors 6502

Joined:
Jan 3, 2009
#10
Just following along in Kochan's book...I don't think I would ever actually do this, but nice to understand how it works.

So, you declare a pointer to pointer to Fraction as the array is really an array of pointers to Fraction??

11. lee1210 Expand Collapse macrumors 68040

Joined:
Jan 10, 2005
Location:
Dallas, TX
#11
Yes. C-Style arrays are always just pointers to the base element of the array. The array in this case contains Fraction *s, so the array is just a Fraction ** pointing to the 0th element.

-Lee

12. mdeh Expand Collapse thread starter macrumors 6502

Joined:
Jan 3, 2009
#12

Yes...that's the confusing part to me. I tried to get a simple [myPt print] to work but could not. The **only** way to get it to even compile was to dereference myPtr.

13. mdeh Expand Collapse thread starter macrumors 6502

Joined:
Jan 3, 2009
#13

Of course..I had forgotten. In this case, element 0 of the array is a pointer to a pointer to a Fraction object!!

Thank you all...I hope Steve can give his opinion.

14. skochan Expand Collapse macrumors regular

Joined:
Apr 1, 2006
Location:
California
#14
Yes. Recall from the end of the chapter that an "object" in Objective-C is really a pointer to a structure that contains the object's declared instance variables, as well as its inherited ones. So the fraction array you are setting up is an array of pointers. If you want to make a pointer to point to an element in the array, it would be declared as a pointer to a pointer, or as Fraction **

So to add two consecutive Fraction objects stored in an array pointed to by fractsPtr you would write

Code:
`result = [*fractsPtr add: *(fractsPtr + 1)];`
The declaration of fractsPtr and the print method call should also be corrected, as you have discovered.

This error has been there since the first edition (written about 5 years ago), and you are the first to discover this. It's a credit to your detailed study of the subject.

Cheers,

Steve Kochan

15. kalimba Expand Collapse macrumors regular

Joined:
Jun 10, 2008
#15
I haven't actually tried to compile this and I'm just wondering aloud here, but would this be a suitable fix to the problem:

Code:
```#import "Fraction.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

Fraction *fractArr[10], *fractPtr;
fractPtr = fractArr[COLOR="Red"][0][/COLOR]; [COLOR="Red"]// make fractPtr point to the first Fraction * in the array[/COLOR]

Fraction *aFrac = [ [ Fraction alloc] initWith: 3 over: 4];
Fraction *bFrac = [ [ Fraction alloc] initWith: 7 over: 8];

fractArr[0] = aFrac;
fractArr[1] = bFrac;

[fractPtr print];
fractPtr++;
[fractPtr print];

[aFrac release];
[bFrac release];
[pool drain];
return 0;
}```

16. gnasher729 Expand Collapse macrumors G5

Joined:
Nov 25, 2005
#16
It will go very badly wrong.

Maybe this helps you to clean up the confusion: If you compare Cocoa and Carbon, there are NSString* in Cocoa and CFStringRef in Carbon, and they are roughly the same thing. NSString* is officially a pointer, but you don't use it as a pointer, you just use it as a thing that somehow in unidentifiable ways refers to a string, just like a CFStringRef is a thing that somehow in unidentifiable ways refers to a string.

typedef Fraction* FractionRef;

and use FractionRef instead of Fraction*. Then things should suddenly become a lot, lot clearer. Once it is clear that way, change FractionRef back to Fraction* and do the translation in your head only.

And you never, ever perform pointer arithmetic on an NSObject*. It is always wrong. (CFStringRef is defined in a way that makes pointer arithmetic illegal).

17. mdeh Expand Collapse thread starter macrumors 6502

Joined:
Jan 3, 2009
#17
it crashes when you try and print.

Now I am no expert on pointers...and there are others that might want to chime in, but here is what I perceive as problems with the code you suggested.

Code:
```#import "Fraction.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

Fraction *fractArr[10], *fractPtr;  ```
OK...So fractPtr is a ptr to a Fraction Object. Now, I am not sure, as I have not reached the end of the chapter, but I suspect the pointer value here is the address of the first element of the object...which is not really what you want.

Code:
`fractPtr = fractArr[COLOR="Red"][0][/COLOR]; [COLOR="Red"]// make fractPtr point to the first Fraction * in the array[/COLOR]`
As you say, point to the fraction object stored in the first element of the array

.....snip....

Code:
```	[fractPtr print];
fractPtr++;
```
Well, the first easy one is fractPtr++. I think the **intention** is to advance the pointer to the next element in the array, but I think what you are doing is advancing it in the object, which is not what you want.

As to why [fractPtr print] crashes with a sig error is more of a mystery to me. But...it certainly does crash. I am not sure what the diff is between dereferencing a ptr declared as Fraction **fptr and your method..perhaps others can chime in.

18. eddietr Expand Collapse macrumors 6502a

Joined:
Oct 29, 2006
Location:
Virginia
#18
The reason it crashes is this:

Code:
```fractPtr = fractArr[0];
```
So fractPtr is a copy of the contents of the first element of the array. The compiler allows this because the array is defined as an array of Fraction*, and fractPtr is declared as a Fraction*. So far so good.

But, the copy (of the "pointer") is made before fractArr[0] has anything meaningful in it. It isn't until later that fractArr[0] contains an actual pointer to a Fraction object.

19. mdeh Expand Collapse thread starter macrumors 6502

Joined:
Jan 3, 2009
#19
Of course!! Good point. If I do the initialization prior to the assignment, it **does** print the first fraction. But, the essence of the pointer is to parse the **array** so advancing the pointer as declared cannot be done???? Besides, this is the intention of the code, so, if he pointer is declared as it is, one cannot achieve the purpose set out.
Thanks for catching the cause of the crash...makes it more interesting to understand the overall effect.

20. kalimba Expand Collapse macrumors regular

Joined:
Jun 10, 2008
#20
Eeep. Sorry for adding noise to the discussion. I was looking at the problem from the standpoint of eliminating the compiler warning, without looking at what effect it have on proper program execution. With the latter now in mind, would this work as expected?:

Code:
```#import "Fraction.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

Fraction *fractArr[10], *fractPtr;
fractPtr = fractArr[COLOR="Red"][0][/COLOR]; // make fractPtr point to the first Fraction * in the array

Fraction *aFrac = [ [ Fraction alloc] initWith: 3 over: 4];
Fraction *bFrac = [ [ Fraction alloc] initWith: 7 over: 8];

fractArr[0] = aFrac;
fractArr[1] = bFrac;

[[COLOR="Red"]*fractPtr[/COLOR] print];
fractPtr++;
[[COLOR="Red"]*fractPtr[/COLOR] print];

[aFrac release];
[bFrac release];
[pool drain];
return 0;
}```

21. eddietr Expand Collapse macrumors 6502a

Joined:
Oct 29, 2006
Location:
Virginia
#21
Yes, you're right about that. It won't achieve the objective. As least as far as I understand what the objective was without having looked at the book yet. (I don't have the book)

22. eddietr Expand Collapse macrumors 6502a

Joined:
Oct 29, 2006
Location:
Virginia
#22
Still a couple of issues, though.

fractPtr needs to be a pointer to the pointer, not just a copy of the Fraction* as you have it now. So you still want:

Code:
```Fraction **fractPtr;

fractPtr = fractArr;
```
or alternatively:

Code:
```fractPtr = &fractArr[0];
```
The point being you're not looking for the contents of fractArr[0], you're looking for the address of fractArr[0].

23. gnasher729 Expand Collapse macrumors G5

Joined:
Nov 25, 2005
#23
I took the liberty to add a typedef to make things clearer. A Fraction* refers to a single object that is allocated at some unknown location in memory.

Code:
```#import "Fraction.h"
typedef Fraction* FractionRef;

int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

FractionRef fractArr[10], FractionRef;
fractPtr = fractArr[COLOR="Red"][0][/COLOR];
// doesn't make fractPtr point to the first FractionRef in the array.
// fractPtr is supposed to _be_ a FractionRef.

FractionRef  aFrac = [ [ Fraction alloc] initWith: 3 over: 4];
FractionRef bFrac = [ [ Fraction alloc] initWith: 7 over: 8];

fractArr[0] = aFrac;
fractArr[1] = bFrac;

// This is nonsense; you shouldn't dereference a FractionRef.
[[COLOR="Red"]*fractPtr[/COLOR] print];
// This is nonsense, you can't increment a FractionRef in a meaningful way
fractPtr++;
// This is nonsense; you shouldn't dereference a FractionRef.
[[COLOR="Red"]*fractPtr[/COLOR] print];

[aFrac release];
[bFrac release];
[pool drain];
return 0;
}```

24. eddietr Expand Collapse macrumors 6502a

Joined:
Oct 29, 2006
Location:
Virginia
#24
It might be useful if mdeh posted some background on what the assignment was. But I'm guessing the point of the assignment was to illustrate how you can move through the array using pointer arithmetic.

So if that was the point (mdeh correct me if I'm wrong), then fractPtr should be a Fraction**. And then you can use fractPtr++ to iterate through the array of Fraction*. And of course then [*fractPtr whatever] would be correct.

25. mdeh Expand Collapse thread starter macrumors 6502

Joined:
Jan 3, 2009
#25

Well..it's not really an assignment (yet!!) but the purpose was to show how pointers can be used to iterate through an array ( as Steve's note indicates). From the text it was clear that the idea of fractPtr was to point to an **array** of fraction objects and **not** to the objects themselves. Besides, the real crux if one does not point to the array is how does one then iterate the array. Hope that helps.