return fixed size array

2019-07-29 05:41发布

问题:

Sometimes I want to return an array of something, I know what I should do it let the caller to make the array and modify the array in the method. But it turns out this works?

@interface Test : NSObject
@end

@implementation Test

- (CGPoint[2])test {
    CGPoint p1 = {1, 2};
    CGPoint p2 = {3, 4};
    return (CGPoint[2]) {p1, p2};
}

- (int[2])test2 {
    int i = 1;
    int i2 = 2;
    return (int[2]){i, i2};
}

- (int[5])test3 {
    int i = 1;
    int i2 = 2;
    return (int[5]){i, i2, 3, 4, 5};
}

@end

@implementation testTests

- (void)testExample
{
    Test *t = [Test new];
    CGPoint p = [t test][0];
    CGPoint p2 = [t test][1];
    CGPoint *ps = [t test];
    CGPoint p3 = ps[0];
    CGPoint p4 = ps[1];
    NSLog(@"%@ %@ %@ %@", NSStringFromCGPoint(p), NSStringFromCGPoint(p2), NSStringFromCGPoint(p3), NSStringFromCGPoint(p4));
    // {1, 2} {3, 4} {1, 2} {3, 4}

    {
        Method m = class_getInstanceMethod([Test class], @selector(test));
        const char *rettype = method_copyReturnType(m);
        NSUInteger size = 0;
        NSGetSizeAndAlignment(rettype, &size, NULL);
        NSLog(@"%s %d", rettype, size); // [2{CGPoint=ff}] 16
    }

    {
        Method m = class_getInstanceMethod([Test class], @selector(test2));
        const char *rettype = method_copyReturnType(m);
        NSUInteger size = 0;
        NSGetSizeAndAlignment(rettype, &size, NULL);
        NSLog(@"%s %d", rettype, size); // [2i] 8
    }

    {
        Method m = class_getInstanceMethod([Test class], @selector(test3));
        const char *rettype = method_copyReturnType(m);
        NSUInteger size = 0;
        NSGetSizeAndAlignment(rettype, &size, NULL);
        NSLog(@"%s %d", rettype, size); // [5i] 20
    }
}

@end

So it looks like I can return an array of something directly like return a struct. As NSGetSizeAndAlignment suggested, the return value is actually the whole array not just a pointer. (16 for CGPoint[2], 8 for int[2], 20 for int[5])

But to actually use the returned value, I can only do this

CGPoint *ps = [t test];

and here ps is just a pointer. So the question is that can I safely return array like this? where is the array allocated at? In stack of callee function or in stack of caller function? Is true that whole array is copied from callee function or like struct the complier allocated the array in caller function automatically?

I am using Xcode 4.6.2


Update

Looks like this only woks for 32bits app, which includes iOS app (ARM), iOS simulator (x86) and 32bits OSX app.

回答1:

C does not support returning an array. Why this apparently works for you I've no idea. I entered your code, added the missing methods/imports, fixed the format warnings, tweaked the first NSLog - none of which changes the code in any significant way - and the result is:

p: {0.000000, 0.000000}
p2: {49923903424063506281151950574939686583391145211556805465272890158614887709849874883833083042335634395057539888815696938380880844447770099086084465717389090095104.000000, 5053651901346670085892443969395884608467030378650281488576308318306304.000000}
p3: {0.000000, 0.000000}
p4: {0.000000, 0.000000}

In short, garbage - which is what you'd expect.

That is with Xcode 4.6.2 on 10.8.4 using the Apple LLVM default compiler. Switch to the gcc-llvm compiler and it won't even compile - the error is methods cannot return arrays. For completeness also tested the C11 language options (Apple LLVM) - runs and produces garbage as above.

If you really need to pass arrays by value you can do this using a simple trick: primitive types (int, float, etc.) and structures are all passed by valued in C, so you can pass an array by value by wrapping it in a struct:

typedef struct
{
   CGPoint values[2];
} Array2;

@interface Test2 : NSObject
@end

@implementation Test2

- (Array2)test
{
   CGPoint p1 = {1, 2};
   CGPoint p2 = {3, 4};
   return (Array2) {{p1, p2}};
}


- (void)testExample
{
   Test2 *t = self;
   CGPoint p = [t test].values[0];
   CGPoint p2 = [t test].values[1];
   Array2 ps = [t test];
   CGPoint p3 = ps.values[0];
   CGPoint p4 = ps.values[1];
   NSLog(@"p: %@\np2: %@\np3: %@\np4: %@", NSStringFromCGPoint(p), NSStringFromCGPoint(p2), NSStringFromCGPoint(p3), NSStringFromCGPoint(p4));
}

@end

and this produces:

p: {1.000000, 2.000000}
p2: {3.000000, 4.000000}
p3: {1.000000, 2.000000}
p4: {3.000000, 4.000000}

as it should.

Addendum - Response to Comment

The C Standard disallows the returning of arrays (and functions) - but references to arrays (and functions) are allowed.

For function parameters if the type given is an array (or function) type then it is adjusted to mean reference to array (or function).

Though I don't know that the Standard states it, some compilers (e.g. Apple 4.2, but not GCC 4.2) apply the parameter type adjustment to the return type as well - so you can declare a function to return an array and it is adjusted to be a reference to an array.

Objective-C's metadata is recording the declared, rather than the adjusted, type. This is why you see the results you do when calling method_copyReturnType/NSGetSizeAndAlignment. However the compiling is following the standard, adjusting the type, and not returning arrays by value.

Having said that a compiler could of course choose to implement array-by-value as an extension - as the machinery to do so needs to be there to support struct-by-value anyway. I don't know offhand whether any compiler supports such an extension. I have only tested the 4.2 Intel compilers (Apple & GCC) and they do not (and GCC does not support declaring the return type as an array either, that is a compile time error).

HTH