1. Welcome to the new MacRumors forums. See our announcement and read our FAQ

Assignment problem

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

  1. macrumors 6502

    #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
    
    //  Copyright 2009 __MyCompanyName__. All rights reserved.
    //
    
    #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
    
    //  Copyright 2009 __MyCompanyName__. All rights reserved.
    //
    
    #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. macrumors 6502

    #2
    Have you tried making sure that your initWith: returns a Fraction?

    -(Fraction *) initWith: (int) n over: (int) d;
     
  3. macrumors 6502

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

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

    lee1210

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

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

    #7
    Why don't use just use NSArray?

    Anywho...

    This snippit should help you out:

    Code:
    Fraction *fracArr[10];
    Fraction **fracPtr = fracArr;
    
    // Fill array
    *fracPtr = [[Fraction alloc] init...];
    
    [(*fracPtr) someMethod];
    
     
  8. macrumors 6502

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

    lee1210

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

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

    lee1210

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

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

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

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

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

    gnasher729

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

    Add a line:

    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. macrumors 6502

    #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. macrumors 6502a

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

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

    #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. macrumors 6502a

    #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. macrumors 6502a

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

    gnasher729

    #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. macrumors 6502a

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

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

Share This Page