함수 포인터 & Restricted Pointer

김경주·2023년 2월 2일

C

목록 보기
3/4

함수 포인터

  • C는 포인터가 오직 데이터만 가리키게만 하지 않는다. 함수도 기억 장소를 점유하며 이는 모든 함수가 각 변수들이 주소를 가지는 것처럼 주소를 가지고 있다.
double integrate(double (*f) (double), double a, double b);
double integrate(double f(double), double a, double b);
  • (*f)에서 괄호는 f가 함수를 가리키는 포인터 즉 함수 포인터라는 것을 나타낸다.
  • 컴파일러 관점에서는 위 두 프로토타입은 동일하다.
result = integrate(sin, 0.0, PI / 2);
  • 위 함수 호출시 함수명을 첫 번째 인자로 제공 -> 근데 뒤에 괄호가 따라나오지 않는다? -> C 컴파일러가 함수 호출에 관한 코드 생성 대신 함수를 가리키는 포인터를 만들어낸다. 위 코드는 sin을 호출하지 않고 integrate에 sin을 가리키는 포인터를 보내준다.
    어렵다면 C가 array를 어떻게 다루는지 생각해보자. a의 이름의 array가 있다면, a[i]는 array의 하나의 원소를 표현한다. 그리고 a 그 자신은 array를 가리키는 포인터로서의 역할을 한다. 비슷한 방식으로 f가 하나의 함수라면, C는 f(x)를 함수의 호출로 처리하겠지만 f 그 자체는 함수를 가리키는 포인터로서의 역할을 한다.

함수 포인터는 함수들의 인자로서 유용함뿐만 아니라 데이터를 가리키는 포인터들과 같이 다룰 수 있다.
함수 포인터들을 변수에 저장할 수 있거나 배열의 원소들로서 사용할 수 있으며, 구조체나 공용체의 멤버로서도 사용할 수 있다.

void (*pf)(int); 
pf = f; // f is a function and no need '&'
(*pf)(i); // call f 
pf(i); // call f same as (*pf)(i);

함수 포인터들이 원소인 배열은 여러가지에 응용할 수 있다.

void (*file_cmd[])(void) = {new_cmd,
							open_cmd,
                            close_cmd,
                            close_all_cmd,
                            save_cmd,
                            save_as_cmd,
                            save_all_cmd,
                            print_cmd,
                            exit_cmd
                            };
                            
 (*file_cmd[n])(); // or file_cmd[n]();

Restricted Pointers

  • Restricted pointers의 목적은 만약 p가 후에 수정될 객체를 가리킨다면, 그 객체는 p를 통하지 않고 다른 방식으로 접근이 안된다는 것이다. 객체에 접근할 수 있는 대안들이 있는데 같은 객체를 가리키는 또다른 포인터를 가지거나 이름있는 변수를 가리키는 p를 가지는 것이다. 객체에 접근하는 방식을 하나 이상 가지는 것을 aliasing이라 부른다.
int * restrict p;
int * restrict q;

p = malloc(sizeof(int));
q = p; // legal
*q = 0 // undefined behavior 발생
p와 q는 같은 객체를 가리키고 있다. -> *p와 *q를 aliases라 한다.
  • restrict pointer p가 extern storage class가 아닌 지역 변수로서 선언되어있다면 restrict는 p가 선언된 block에서 실행될 때만 p에 적용된다.
    restrict는 포인터 타입의 함수 파라미터로 사용될 수 있다.(함수 body가 block이니깐) 이러한 경우 함수가 실행될 때만 적용된다.
    file scope(파일 범위)의 포인터 변수에 restrict가 적용될 때에는 프로그램이 끝날 때까지 지속된다.
  • restrict의 사용하는 것에 있어서 정확한 규칙은 다소 복잡하다. 상세한 부분은 표준을 봐야한다. 심지어 alias가 restricted 포인터로 부터 생성되는 상황도 허용된다. 예를 들어 하나의 restricted 포인터 p가 또다른 restricted 포인터 q로 합법적으로 복사될 수 있다. p가 하나의 함수에서 지역변수이고 q가 함수의 body안의 block안에서 정의되어 있는 상황일 때.
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
  • s2는 복사될 데이터를 가르키고 있고 s1은 복사할 목적지를 가르키고 있다. n은 복사될 bytes의 수이다.
    s1과 s2의 restrict의 용법은 복사할 자원과 목적지가 overlap(겹치게) 할 수 없다는 것을 나타낸다. 그치만 이들이 겹치지(overlap) 않는다는 것을 보장하진 않는다.
void *memmove(void *s1, const void *s2, size_t n);
  • memmove는 memcpy와 같다. 다른 점은 memmove는 overlap이 일어난다해도 잘 작동하는 것을 보장한다.
  • C99 이전에는 두 함수의 차이점은 없다.
void *memcpy(void *s1, const void *s2, size_t n);
void *memmove(void *s1, const void *s2, size_t n);
  • C99버전의 memcpy의 프로토타입에서 restrict의 사용은 프로그래머에게 s1과 s2가 overlap하지 않는 객체를 가리켜야하는 것을 알려주거나 아니면 함수의 동작을 보장하지 않는다.

  • restrict는 컴파일러에게 좀 더 효율적인 코드를 생산할 수 있게 정보를 제공한다.(최적화, optimization)

출처: K.N.KING C PROGRAMMING A MODERN APPROACH

profile
Hello everyone

0개의 댓글