본문 바로가기

카테고리 없음

Const Keyword

공부하다가 좋은 부분을 발견해서 적습니다.

 

const 의 올바른 사용은 에러를 막는데 좋은 역할을 할 수 있다.

 

void func(Hello* const hello)
{

    hello = NULL; //error
    hello = (Hello*)malloc(sizeof(Hello)); //error
    Hello h;
    hello = &h; //error

}

 

Hello* const hello의 의미는 hello라는 포인터가 다른 객체를 가리키도록 바뀔 수가 없다는 의미이다. 

이렇게 const를 붙이는 것이 의미가 있는 것이 위의 함수가 Hello 객체를 위한 함수라면 그 객체에게 일을 시키는 것이 목적이므로 저렇게 함수 안에서 자신이 가리키는 객체가 바뀔 이유가 없고, 만약 바뀐다면 그것이 에러인 상황일 확률이 높기 때문이다. 

 

이번에는 다른 예제를 보자

 

위에서 포인터 변수를 쓰는 이유가 (value를 쓰는 것보다) 효율적이면서 원래 객체의 값을 변경할 수 있기 때문이라고 얘기를 했다. 하지만, 원래 객체의 값을 변경시킬 수 있다는 점은 어떤경우에는 그 함수를 사용하는데 주저 하게 할 수 있다. 예를들어 어떤 라이브러리에 있는 함수를 사용하려고 하는데 이 함수의 원형이 다음과 같다고 하자.

 

typedef struct FooTag

{

	int a;
    
}Foo

 

void bar(Foo* foo);

 

bar 함수는 Foo 객체의 포인터를 받아서 무엇인가 하는 함수이다. 물론, 라이브러리를 쓴다면 보통 저 bar 함수에서 무슨 일을 하는지는 메뉴얼이나 헬프 파일에 나오게 되지만 일반적으로 함수 내부 implementation에 대해서 나오진 않는다. 

 

Foo foo;
foo.a = 0;
bar(&foo);

 

이런식으로 쓸 때, foo가 가지고 있는 a의 값은 0이다. 그런데, bar 함수 내에서 이 값이 예상치 못하게 바뀐다면

문제가 된다. 그래서 const 키워드는 값이 바뀌지 않는다는 보장을 위해 사용된다.

void bar(const Foo* foo);

 

다음과 같은 시도는 컴파일러 에서 에러로 처리한다.

void bar(const Foo* foo){

	foo->a =1; //error

}