Starbucks Caramel Frappuccino
본문 바로가기
  • 그래 그렇게 조금씩
iOS Developer/Objective-C

10. Objective-C Arrays 배열

by Toughie 2024. 5. 7.

KAKAO-Choonsik

 

Objective-C에서 배열은 '같은 타입'의 요소들이 '연속적으로 저장'된 '고정된 사이즈'의 자료구조이다.

배열의 원소는 연속적인 메모리 공간에 저장되어 있어  index로 접근 가능하며(0이 맨 첫번째) 

가장 낮은 주소가 첫 번째 요소, 가장 높은 주소가 마지막 요소를 가리킨다.


배열 선언

요소의 타입과 배열의 사이즈를 명시적으로 작성해준다.

(배열의 사이즈는 0초과의 정수 리터럴이며, 타입은 크게 구애받지 않는다.)

type arrayName [arraySize];
//declare
double balance[10];
//초기화 해주지 않아서 0이나 쓰레기 값이 들어있다.

배열 초기화

double balance[5] = {1000.0, 2.0, 3.0, 50.0, 100.0};
//사이즈를 생략하면 배열의 요소 개수에 맞게 사이즈가 알아서 정해진다. (unsized array)
int myArray[] = {1, 2, 3, 4, 5};

 

배열 요소 접근

0부터 시작해서 (사이즈-1)까지 요소 접근이 가능하며 대괄호 문법을 사용한다.

#import <Foundation/Foundation.h>

int main(void) {
    int n[10];
    int i,j;
    
    for (i =0; i<10; i++) {
        n[i] = i + 100;
    }
    
    for(j=0; j<10; j++) {
        NSLog(@"ELEMENT[%d] = %d",j,n[j]);
    }
    return 0;
}

다차원 배열(Multi-imensional arrays)

objective-c도 다차원 배열을 지원한다.

int threedim[5][10][4];

int threedim[2][3][3] = {
    { // 첫 번째 2차원 배열
        {0, 1, 2}, // 첫 번째 1차원 배열
        {3, 4, 5}, // 두 번째 1차원 배열
        {6, 7, 8}  // 세 번째 1차원 배열
    },
    { // 두 번째 2차원 배열
        {9, 10, 11},
        {12, 13, 14},
        {15, 16, 17}
    }
};

 

기본적으로는 2차원 배열을 가장 많이 사용하며, row 구분을 위한 nested bracket은 optional이다.

사용하는게 가독성이 훨씬 좋다고 생각한다.

#import <Foundation/Foundation.h>

int main(void) {
    int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
    
    int b[3][4] = {
        {0,1,2,3},
        {4,5,6,7},
        {8,9,10,11}
    };

    int i,j;
    //배열 전체 바이트 수를 첫 행의 사이즈로 나누면 행의 개수가 나온다.
    int rows = sizeof(a) / sizeof(a[0]);
    int cols = sizeof(a[0]) / sizeof(a[0][0]);
    
    for(i=0; i<rows; i++) {
        for(j=0; j<cols; j++) {
            NSLog(@"a[%d][%d] = %d",i,j,a[i][j]);
        }
    }

    return 0;
}

함수에 배열 전달

1. 포인터 전달, 2. 배열 전달(sized), 3.배열 전달(unsized)

objective-c에서 함수에 배열을 전달하면, 이는 배열의 시작 메모리 주소(0번째 요소) 즉 포인터가 전달된다.

배열변수는 포인터 상수이기 때문이다.

따라서 해당 포인터만 가지고는 배열의 끝을 알 수 없기 때문에(값 읽기는 타입을 통해 수행하지만) 

배열의 크기 또한 함께 전달해줘야 한다.

그래서 사실 위 방식들은 다 동일한 방식이라고 볼 수 있다.

#import <Foundation/Foundation.h>

@interface MyClass:NSObject
-(double) getAverage1:(int []) arr andSize:(int) size;
-(double) getAverage2:(int *) arr andSize:(int) size;
@end

@implementation MyClass

- (double)getAverage1:(int[])arr andSize:(int)size {
    int i;
    double avg;
    double sum = 0;
    
    for(i=0; i <size; i++) {
        sum += arr[i];
    }
    avg = sum / size;
    return avg;
}

- (double)getAverage2:(int *)arr andSize:(int)size {
    int i;
    double avg;
    double sum = 0;
    
    for(i=0; i <size; i++) {
        sum += arr[i];
    }
    avg = sum / size;
    return avg;
}
@end

int main(void) {
    MyClass *myClass = [[MyClass alloc] init];
    int balance[5] = {1,2,3,4,5};
    double avg1;
    double avg2;
    int size = sizeof(balance) / sizeof(int);
    
    avg1 = [myClass getAverage1:balance andSize:size];
    avg2 = [myClass getAverage2:balance andSize:size];
    
    NSLog(@"avg1: %lf, avg2: %lf",avg1,avg2);
    
    return 0;
}

 

결국 포인터를 전달하는 것이기 때문에 똑같다.


함수의 반환값인 배열

기본적으로 배열 전체를 반환하는 것은 불가능하다.

하지만 배열에 대한 포인터를 반환할 수 있다. 

배열은 객체니까, 포인터만 있으면 찾아가서 값을 사용할 수 있다.

 

but 함수 내부에서 만든 배열의 포인터를 그냥 return 해버리면

함수가 종료되면서 스코프가 벗어나 배열 포인터 변수가 소멸해버린다.

이는 메모리 누수의 원인이 되기도 하고 올바른 동작을 하지 않는다.

이렇게 스택 메모리에 있는 지역변수를 반환하려 한다고 친절하게 경고창을 띄워준다.

따라서 지역변수를 static으로 정의해서 살려놔야 한다. 

 

랜덤한 숫자 배열을 만들어서 반환하는 예제

#import <Foundation/Foundation.h>

@interface RandomClass:NSObject
-(int *) getRandom;
@end

@implementation RandomClass
-(int *) getRandom {
    static int r[5];
//    int r[5];
    int i;
    //시드 설정
    srand( (unsigned)time(NULL));
    for(i = 0; i<5; i++) {
        r[i] = rand();
        NSLog(@"r[%d] = %d\n", i, r[i]);
    }
    
    return r;
}
@end

int main(void) {
    int *p;
    int i;
    
    RandomClass *randomClass = [[RandomClass alloc] init];
    p = [randomClass getRandom];
    for(i =0; i < 5; i++) {
        NSLog(@"*(p + %d) : %d\n", i, *(p + i));
    }
    
    return 0 ;
}

배열은 포인터 상수?

double *p;
double myArray[10];

p = myArray;

 

여기서 myArray는 배열의 첫번째 요소를 가리키는 상수 포인터이다.

즉 myArray는 &myArray[0]와 동일하다는 의미이다. 첫번째 요소의 메모리 주소!

 

따라서 위와 같이 double 포인터변수에 myArray를 할당할 수 있는 것이다.

 

#import <Foundation/Foundation.h>

int main(void) {
    int myArray[5] = {1,2,3,4,5};
    int *p;
    int i;
    
    p = myArray;
    for(i = 0; i < 5; i++) {
        NSLog(@"*(p + %d) = %d",i,*(p+i));
    }
    
    return 0;
}

 

포인터, 그리고 *연산자를 통해 값을 참조하는 것은 C와 완전 동일하다.