본문 바로가기

개발/Objective-C

* <- 이넘의 정체


참고로 이건 걍 검색하다 얻어 걸린 내용

 

프로그램이 실행하기 전에 로고를 보여주는 윈도우를 보통 Splash Screen 라고 부른다.

 

Xcode에 코딩을 하면서 항상 궁금한 게 있었다.

 

NSArray* array ;

 

이런 객체 선언에서 * 이넘의 정체였다.

하지만 검색도 잘안되고 개략적으로 아는 바가 없어 내심 때려맞쳐 생각하고 있던 중 얻어 걸린 내용이 있다.

 

출처 : 예제로 배우는 C 언어의 기초 중 10.1 포인터 변수의 정의

 

' 포인터는 변수의 일종으로 데이터를 갖고 있는 것이 아니라 메모리의 주소(address)를 표현하는 자료형이다.

  포인터 변수는 어떤 데이터가 기억된 장소의 주소를 기억할 수 있는 변수를 말한다.

  즉, 포인터가 가리키는 대상은 바로 어떤 데이터가 저장되는 메모리의 주소이며,

  포인터 변수의 내용은 데이터가 아니라 번지이다.

  이러한 번지를 이용하여 배열이나 구조체 데이터를 직접 메모리에서 꺼내거나 기억할 수 있다.

  포인터 변수를 선언하기 위한 일반 형식은 다음과 같다

 

  [형식] type *var_name;

           Int *p. float *q. char *r 등으로 선언하여 사용한다.

 

  여기서 type은 포인터 형의 기본형 (base type)으로 포인터가 가리킬 수 있는 객체의 형이다.

  변수 이름(var_name) 앞에 '*' 는 컴퓨터에게 포인터 변수를 생성한다는 것을 알려준다.

  즉, 간접참조(indirection reference) 또는 역참조(dereferencing) 연산자라 불리는 '*' 연산자는

  이 연산자 뒤에 오는 변수의 주소를 반환한다. 예를 들면, p라는 변수가 q라는 다른 변수의 주소를

  가지고 있다면, "p는 q를 가리킨다"라고 말한다. '

 

 대박~! 한번에 쏙 이해가 되지 않아 다시 검색해보았다.

 

 간접연산자 * 는 포인터 변수에 저장된 번지의 내용물을 알려주는 연산자입니다.

 여기에서 주의해야 할 부분은 포인터 변수에 저장된 번지를 알려주는 것이 아니라,

 포인터 변수에 저장된 번지에 저장된 내용물을 알려준다는 점입니다.

 그러므로 *ptr 이라면 n에 저장된 값을 보여주는 것입니다.

 즉, n에 3을 저장했고, ptr에 n의 번지수를 저장했다면

 *ptr은 n의 값을 알아내는 간접 연산자가 됩니다.

 

예제)

 

void main(void)

{

int n; //정수형 변수 n을 선언

int *ptr ;    //정수형 포인터 변수 ptr을 선언

n = 3;   // n 값에 3을 대입

ptr = &a ;   // 포인터 변수 ptr에 n의 번지를 대입

 

printf("변수 n의 값은 n=%d \n", n) ;

printf("변수 n의 번지는 &n=%d \n", &n);

printf("포인터 변수 ptr의 값은 ptr=%d \n", ptr);

printf("ptr에 저장된 번지의 내용물은 *ptr=%d \n",*ptr);

}

 

이 코드를 실행 시키면 다음과 같은 결과가 나올 것이다.

이때 변수 n의 번지로 출력되는 숫자는 프로그램을 실행 시킬 때마다 달라질 수 있다.

 

예제결과 )

변수 n의 값은 n=3

변수 n의 번지는 &n=8778

포인터 변수 ptr의 값은 ptr=8778

ptr에 저장된 번지의 내용물은 *ptr=3

 

*ptr의 *를 간접연산자라고 하며, 포인터변수에 저장된 번지의 내용을 참조할 수 있는 연산자입니다.


이때 사용하는 *를 간접연산자라고 합니다. 간접연산자는 포인터변수에 저장된 번지를 이용하여 그 번지에 저장된 내용을 참조할 수 있는 연산자입니다. 즉 포인터변수에 간접연산자를 붙이면 포인터변수에 저장된 번지에 저장된 내용물을 연산결과로 돌려줍니다.
그렇기 때문에 변수 n의 번지를 나타내는 &n과 변수 n의 번지를 저장한 ptr의 값은 같습니다. 즉 ptr=&n입니다. 그리고 변수 n에 저장된 값이나 변수 n의 번지인 &n에 저장된 값도 같습니다. 그러므로 *ptr=n이 되는 것입니다.

**요약: 간접연산자 *은 포인터변수에 저장된 변수를 이용하여 그 변수의 내용을 간접적으로 참조할 수 있는 연산자이기에 간접연산자라고 합니다.
 
 

// 포인터 변수와 번지의 관계를 살펴보는 예제

#include

void main(void)
{
int x,y; // 정수형 변수 x와 y를 선언
int *ptr; // 정수형 포인터변수 ptr을 선언
x=3; // x에 3을 대입했음.
ptr=&x; // 포인터변수 ptr에 x의 번지를 대입했음.
y=*ptr; // ptr에 저장된 번지에 있는 내용물을 y에 대입
printf("x=%d \n",x ); // x의 값은?
printf("&x=%d \n", &x); // 변수 x의 번지는?
printf("ptr=%d \n", ptr); // ptr의 값은?
printf("*ptr=%d \n",*ptr ); // ptr에 저장된 번지에 저장된 값은?
printf("&ptr=%d \n",&ptr ); // 포인터 변수 ptr에 번지는?
printf("y=%d \n",y ); // y의 값은?
printf("&y=%d \n", &y); // 변수 y의 번지는?
}

이 프로그램의 실행결과는 다음과 같습니다.

x=3
&x=8688
ptr=8688
*ptr=3
&ptr=8684
y=3
&y=8686

x도 3, *ptr도 3, y도 3이 나옵니다. 즉 x에 저장된 값이나 *ptr의 연산결과나 y에 저장된 값이 같은 겁니다. 그럴 수밖에 없는 것이 x의 번지를 ptr에 저장했고, *ptr을 통해서 x의 번지에 있는 내용을 참조하여 y에 대입시켰기 때문입니다.

그리고 x의 번지는 8688번지로 배정받았는데 이 번지를 포인터 변수 ptr에 저장했으므로 &x와 ptr의 값은 같습니다.

마지막으로 세 개의 변수는 각기 다른 번지를 부여받은 것을 알 수 있습니다. 번지연산자 &를 이용하여 &x, &y, &ptr의 세 번지를 알아본 결과 제일 먼저 선언한 x는 8688번지를 부여받았고, y는 8686번지를 부여받았습니다. 그리고 다음에 선언한 변수 ptr은 8684번지를 선언받았음을 알 수 있습니다.
'

 

 

밑에 내용은 검색하다 얻어걸린 것 왠지 필요한 것 같아서 복사 붙이기 신공 ~~

 

이번 강의에서는

그동안 저의 머릿속에서 완전히 뒤죽박죽이 되어서 혼란스럽게 했던

const 키워드를 사용하는 모든 상황을 하나씩 분석해 보도록 하겠습니다.

 

 

const int a=5;
int const a=5;

일단 제일 단순한 const 키워드의 사용 예 입니다.

위 두 개는 똑같은 표현으로,

int 형 변수 a를 선언과 동시에 5로 초기화하고, 변수 a의 값을 못 바꾸게 해라!!

라는 뜻입니다.

앞으로 모든 기본 자로형 키워드를 대신해서 int 라고만 표현하겠습니다.

설명에서 int가 올 자리엔 char, double, float, unsigned int 등등도 다 올 수 있습니다.

제가 두 개씩 제시하는 이유는 두 가지 모두를 혼용해서 쓰라는 것이 아닙니다.

다른 사람이 어떠한 형태로 구현해 놓아도 이해할 수 있어야 한다는 뜻입니다. (어디서 많이 들어본 말 같지 않나요? ㅎ)

그리고 제가 두 개를 제시한다면 가급적 위에 있는 것을 사용하는 것이 좋을 거라고 생각합니다.

const 선언은 왼쪽에 있을수록 가독성이 좋은 거 아닌가요? (개인의 취향인가...)

 

 

const int array[10]={1,2,3,4,5,6,7,8,9,10};

int const array[10]={1,2,3,4,5,6,7,8,9,10};

배열 요소의 값을 바꾸지 못하게 하는 const 선언입니다.

 

 

int* const pa=&a;

포인터 pa를 선언하고, 그 포인터를 오른쪽에 있는 주소 값으로 상수화합니다!!!

포인터는 [하나의 변수의 주소] 만 지닐 수 있게 되지요.

즉, 포인터는 오직 한 주소만 가리키게 됩니다.

따라서 포인터 연산을 통해 포인터가 다른 주소를 가리키게 하는 것이 불가능합니다.

단!!! 포인터가 가리키는 그 변수의 값 만큼은 마음껏 접근해서 변경시킬 수 있습니다!!!

 

 

const int *pa;

int const *pa;

가리키는 변수의 값을 변경하지 못하게 하는 const 포인터를 선언합니다.

이 포인터는 여러 변수의 주소값을 가질 수 있기 때문에, (여러 변수를 가리킬 수 있기 때문에)

꼭 선언과 동시에 초기화 할 필요는 없습니다.

단!!! 포인터가 가리키는 대상을 완전히 값을 변경 불가능하게 하는 것이 아니라,

정확히 말하면 포인터 pa를 통해서 값을 변경시키는 것만 막는 것!!! 입니다.

 

두번째 표현은 마치 좀 전에 소개했던 표현과 비슷합니다.

 

int* const pa=&a;         그리고           int const *pa;

(포인터를 상수화!!!)                          (포인터가 가리키는 대상을 상수화!!)

 

헷갈리시죠? 조심하셔야 합니다. 아주 비슷하게 보이지만 상수화되는 대상이 다르더군요.

(직접 실험해보고 쓰는 강의입니다.)

제가 const 선언은 되도록 왼쪽에 선언할수록 좋다고 한 이유중 하나입니다.

 

 

const int *pa=&a;

이것은 포인터도 상수화하고, 포인터가 가리키는 대상도 상수화합니다.

즉, 포인터는 하나만 가리키고, 그 가리키는 대상의 값도 못 바꾸게 하는 것입니다.

 

 

함수의 인자에 붙은 const 선언!!

void func(const int x, const int y)

{

     ........어찌고저찌고..........

}

이는, 함수 내에서 x과 y를 상수화 하겠다는 이야기입니다.

위 함수를 풀이하면 다음과 같다고 할 수 있죠.

 

void func()

{

     const int x = 첫번째인자;

     const int y = 두번째인자;

     .........어찌고저찌고..............

}

 

int형 변수 x를 선언해서 함수에 첫번째로 전달되는 인자로 초기화하고, 그 값을 못 바꾸게 하는 뜻이죠.

알고보면 전혀 다를게 없는, 아주 간단한 내용입니다.

 

 

 

void (*const pfunc) (void) = func;

(입력 형태가 void 이고 출력 형태가 void인 함수를 가리킬 수 있는) 함수 포인터를 정의합니다.

const 이기 때문에 이 함수포인터는 하나의 함수만 가리킬 수 있습니다!!!

 

 

 

────────────────────────────────────────────────────────

여기까지는 C언어, C++ 모두 다 적용되는 내용이였습니다.

이제부터는 C++ 에서만 적용되는 const 문법 소개입니다.

────────────────────────────────────────────────────────

 

 

const int &ref=a;

int const &ref=a;

이것은, 레퍼런스 ref를 이용해서 a의 값을 변경시키는 것을 막는 것 입니다.

a 자체를 상수화하는것이 아닙니다!

 

 

 

자!! 이제 Car 라는 이름의 클래스(구조체) 하나를 선언하겠습니다.

 

class Car

{

public:

     int speed;

     void FUNC (void) const;

     {

     }

}

 

여기서 멤버함수 FUNC 옆에 붙은 const 는 다음과 같은 뜻을 지닙니다.

"이 함수에서는 멤버변수의 값을 변경하지 않겠다!!!!"

따라서 이 FUNC 이라는 함수 내에서 멤버변수인 speed 의 값을 변경시킨다면, 컴파일 에러가 나게 됩니다.

이러한 함수를 const함수라 합니다.

 

또한, 이러한 const 함수 내에서는, const 함수만 호출이 가능합니다.

const가 아닌 함수가 아무런 기능을 하지 않는 함수라도, 혹시 모르니 그냥 const 함수 내에선 const 함수만 호출이 가능하게 해 놓았습니다.

 

 

 

const Car sonata(10);    //  생성자가 정의되어 있을 때 멤버변수 초기화 방법.

const Car sonata={10};  // 생성자가 정의되어 있지 않을 때의 멤버변수 초기화 방법. (배열처럼)

sonata 라는 Car 객체를 만드는데, 앞에 붙은 const 는 다음과 같은 뜻을 지닙니다.

"이 객체는 멤버변수의 값을 초기화한 후 더 이상 멤버변수의 값을 변경하지 않겠다!!"

이렇게 만들어진 객체를 const객체 라고 합니다.

이와 같은 const 객체의 특징 때문에, const 객체 내에서는 const 함수의 호출만 허용됩니다.

 

 

 

 

이상으로 제가 알고 있는 const 선언을 모두 나열해 보았는데요,

지금까지 알아본 const 선언엔 전부 다음과 같은 공통점이 있습니다.

 

★ const로 정의된 데이터는, 어떠한 일이 있어도 변경으로부터 안전하게 보호된다!!! ★

 

즉, 예를들어 const 로 a라는 변수를 상수화했는데,

포인터 pa로 a를 가리키게 한 후, 포인터를 이용해서 a의 값을 변경시킬 수 있다면 a의 상수화는 아무런 의미가 없게 됩니다.

다행히도 C,C++은 그런 요상한 방법으로 const화된 데이터를 조작시키는일을 완전히 차단시키기 때문에 안심해도 됩니다.

 

 

참고 : http://cafe.naver.com/lovesketch.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=156821

         http://cafe.naver.com/cstudyjava.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=2964