PDA

View Full Version : Assignment problem

mdeh
Jan 28, 2009, 08:38 PM
I get a warning which I cannot understand.

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

//
// 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

//
// 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

#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

North Bronson
Jan 28, 2009, 09:26 PM
Have you tried making sure that your initWith: returns a Fraction?

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

mdeh
Jan 28, 2009, 09:29 PM
Have you tried making sure that your initWith: returns a Fraction?

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

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.

kpua
Jan 28, 2009, 09:34 PM
fractArr is a Fraction ** since it is a pointer to an array.

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

lee1210
Jan 28, 2009, 09:40 PM
fractArr is a Fraction ** since it is a pointer to an array.

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

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

mdeh
Jan 28, 2009, 09:41 PM
fractArr is a Fraction ** since it is a pointer to an array.

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

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. :-)

kpua
Jan 28, 2009, 09:47 PM
Why don't use just use NSArray?

Anywho...

Fraction *fracArr[10];
Fraction **fracPtr = fracArr;

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

[(*fracPtr) someMethod];

mdeh
Jan 28, 2009, 09:48 PM
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

Lee, you are correct...as always!!! :-)

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

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?

lee1210
Jan 28, 2009, 09:51 PM
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

mdeh
Jan 28, 2009, 09:52 PM
Why don't use just use NSArray?

Just following along in Kochan's book...I don't think I would ever actually do this, but nice to understand how it works.

Anywho...

Fraction *fracArr[10];
Fraction **fracPtr = fracArr;

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

[(*fracPtr) someMethod];

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

lee1210
Jan 28, 2009, 09:55 PM
So, you declare a pointer to pointer to Fraction as the array is really an array of pointers to Fraction??

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

mdeh
Jan 28, 2009, 09:57 PM
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

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.

mdeh
Jan 28, 2009, 10:00 PM
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

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.

skochan
Jan 29, 2009, 08:36 AM
So, you declare a pointer to pointer to Fraction as the array is really an array of pointers to Fraction??

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

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

kalimba
Jan 29, 2009, 10:37 AM
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:

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

Fraction *fractArr[10], *fractPtr;
fractPtr = fractArr[0]; // 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;

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

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

gnasher729
Jan 29, 2009, 01:22 PM
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:

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).

mdeh
Jan 29, 2009, 01:22 PM
I haven't actually tried to compile this

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.

#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.

fractPtr = fractArr[0]; // make fractPtr point to the first Fraction * in the array

As you say, point to the fraction object stored in the first element of the array

.....snip....

[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.

eddietr
Jan 29, 2009, 01:58 PM
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.

The reason it crashes is this:

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.

mdeh
Jan 29, 2009, 02:11 PM
The reason it crashes is this:

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.

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.

kalimba
Jan 29, 2009, 02:41 PM
It will go very badly wrong.
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?:

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

Fraction *fractArr[10], *fractPtr;
fractPtr = fractArr[0]; // 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;

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

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

eddietr
Jan 29, 2009, 03:58 PM
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.

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)

eddietr
Jan 29, 2009, 04:03 PM
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?:

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

Fraction *fractArr[10], *fractPtr;
fractPtr = fractArr[0]; // 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;

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

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

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:

Fraction **fractPtr;

fractPtr = fractArr;

or alternatively:

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].

gnasher729
Jan 29, 2009, 04:10 PM
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?:

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.

#import "Fraction.h"
typedef Fraction* FractionRef;

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

FractionRef fractArr[10], FractionRef;
fractPtr = fractArr[0];
// 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.
[*fractPtr print];
// This is nonsense, you can't increment a FractionRef in a meaningful way
fractPtr++;
// This is nonsense; you shouldn't dereference a FractionRef.
[*fractPtr print];

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

eddietr
Jan 29, 2009, 04:19 PM
snip

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

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.

mdeh
Jan 29, 2009, 04:39 PM
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.

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.

eddietr
Jan 29, 2009, 05:10 PM
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.

Makes sense. So Fraction **fractPtr sounds like the way to go.

There are other ways to iterate through the array, but I see the point of the exercise. It's a good concept to understand.