Pointer declaration
포인터는 다른 타입의 함수나 객체를 참조하는 객체의 한 종류로, 한정자가 추가될 수 있습니다. 포인터는 아무것도 참조하지 않을 수도 있으며, 이는 특별한 널 포인터 값으로 표시됩니다.
목차 |
구문
포인터 선언의 선언 문법 에서, type-specifier 시퀀스는 가리키는 대상의 타입(함수 또는 객체 타입일 수 있고 불완전할 수 있음)을 지정하며, declarator 는 다음과 같은 형태를 가집니다:
*
attr-spec-seq
(선택 사항)
qualifiers
(선택 사항)
declarator
|
|||||||||
여기서 declarator 는 선언되는 포인터의 이름을 지정하는 식별자일 수 있으며, 다른 포인터 선언자(이는 포인터에 대한 포인터를 나타냄)를 포함할 수 있습니다:
float *p, **pp; // p는 float를 가리키는 포인터입니다 // pp는 float를 가리키는 포인터를 가리키는 포인터입니다 int (*fp)(int); // fp는 int(int) 타입의 함수를 가리키는 포인터입니다
*
와 식별자(또는 다른 중첩 선언자) 사이에 나타나는
한정자
는 선언되는 포인터의 타입을 한정합니다:
int n; const int * pc = &n; // pc는 const int에 대한 비-const 포인터입니다 // *pc = 2; // 오류: 캐스트 없이 pc를 통해 n을 변경할 수 없습니다 pc = NULL; // OK: pc 자체는 변경할 수 있습니다 int * const cp = &n; // cp는 비-const int에 대한 const 포인터입니다 *cp = 2; // cp를 통해 n을 변경하는 것은 OK // cp = NULL; // 오류: cp 자체는 변경할 수 없습니다 int * const * pcp = &cp; // 비-const int에 대한 const 포인터에 대한 비-const 포인터
attr-spec-seq (C23) 는 선언된 포인터에 적용되는 속성 들의 선택적 목록입니다.
설명
포인터는 프로그래밍에서 보편적으로 사용되는 간접 참조(indirection)를 위해 사용됩니다; 포인터는 참조에 의한 전달(pass-by-reference) 의미론을 구현하고, 동적 storage duration 을 가진 객체에 접근하며, "선택적" 타입(널 포인터 값을 사용하여), 구조체 간의 집합 관계, 콜백(함수 포인터 사용), 일반 인터페이스(void 포인터 사용) 등을 구현하는 데 사용될 수 있습니다.
객체에 대한 포인터
객체에 대한 포인터는 객체 타입(불완전할 수 있음)의 표현식에 적용된 주소 연산자 의 결과로 초기화될 수 있습니다:
int n; int *np = &n; // int에 대한 포인터 int *const *npp = &np; // non-const int에 대한 const 포인터를 가리키는 non-const 포인터 int a[2]; int (*ap)[2] = &a; // int 배열에 대한 포인터 struct S { int n; } s = {1} int* sp = &s.n; // s의 멤버인 int를 가리키는 포인터
포인터는
역참조 연산자
(단항
*
)의 피연산자로 나타날 수 있으며, 이는 가리키는 객체를 식별하는
lvalue
를 반환합니다:
int n; int* p = &n; // 포인터 p가 n을 가리키고 있음 *p = 7; // n에 7을 저장함 printf("%d\n", *p); // lvalue-to-rvalue 변환이 n에서 값을 읽어옴
struct
및
union
타입 객체에 대한 포인터는
포인터를 통한 멤버 접근
연산자
->
의 좌측 피연산자로 나타날 수도 있습니다.
배열-포인터 암시적 변환 때문에, 배열의 첫 번째 요소에 대한 포인터는 배열 타입의 표현식으로 초기화될 수 있습니다:
int a[2]; int *p = a; // a[0]을 가리키는 포인터 int b[3][3]; int (*row)[3] = b; // b[0]을 가리키는 포인터
배열 요소에 대한 포인터에 대해 특정 덧셈, 뺄셈 , 복합 할당 , 증가 및 감소 연산자가 정의됩니다.
비교 연산자 는 객체에 대한 포인터에 대해 일부 상황에서 정의됩니다: 동일한 주소를 나타내는 두 포인터는 동등하게 비교되고, 두 널 포인터 값은 동등하게 비교되며, 동일한 배열의 요소에 대한 포인터는 해당 요소들의 배열 인덱스와 동일하게 비교되고, 구조체 멤버에 대한 포인터는 해당 멤버들의 선언 순서대로 비교됩니다.
많은 구현에서는 또한 연속된("평면") 가상 주소 공간 내의 주소로 구현된 경우와 같이 임의의 출처를 가진 포인터들의 엄격한 전체 순서 를 제공합니다.
함수 포인터
함수에 대한 포인터는 함수의 주소로 초기화할 수 있습니다. 함수-포인터 변환 으로 인해 주소 연산자는 선택 사항입니다:
void f(int); void (*pf1)(int) = &f; void (*pf2)(int) = f; // &f와 동일함
함수와 달리, 함수에 대한 포인터는 객체이므로 배열에 저장하거나, 복사하거나, 할당하거나, 다른 함수에 인수로 전달하는 등의 작업을 수행할 수 있습니다.
함수에 대한 포인터는 함수 호출 연산자 의 좌변에 사용될 수 있습니다; 이는 가리키는 함수를 호출합니다:
#include <stdio.h> int f(int n) { printf("%d\n", n); return n * n; } int main(void) { int (*p)(int) = f; int x = p(7); }
함수 포인터를 역참조하면 가리키는 함수에 대한 함수 지정자가 생성됩니다:
int f(); int (*p)() = f; // 포인터 p가 f를 가리키고 있음 (*p)(); // 함수 지정자를 통해 함수 f 호출 p(); // 포인터를 통해 직접 함수 f 호출
동등 비교 연산자 는 함수 포인터에 대해 정의되어 있습니다(동일한 함수를 가리킬 경우 동등으로 비교됩니다).
함수 타입의 호환성 은 함수 매개변수의 최상위 한정자(top-level qualifiers)를 무시하기 때문에, 매개변수가 최상위 한정자만 다른 함수 포인터들은 서로 교환 가능합니다:
int f(int), fc(const int); int (*pc)(const int) = f; // 정상 int (*p)(int) = fc; // 정상 pc = p; // 정상
void 포인터
모든 타입의 객체에 대한 포인터는 암시적으로 변환될 수 있습니다 void 포인터로 (선택적으로 const 또는 volatile 한정자 포함), 그리고 그 반대도 가능합니다:
int n=1, *p=&n; void* pv = p; // int*에서 void*로 변환 int* p2 = pv; // void*에서 int*로 변환 printf("%d\n", *p2); // 1을 출력
void 포인터는 알려지지 않은 타입의 객체를 전달하는 데 사용되며, 이는 일반적인 인터페이스에서 흔히 볼 수 있습니다: malloc 은 void * 을 반환하고, qsort 는 두 개의 const void * 인수를 받는 사용자 제공 콜백을 기대합니다. pthread_create 는 void * 을 받고 반환하는 사용자 제공 콜백을 기대합니다. 모든 경우에서, 사용 전에 포인터를 올바른 타입으로 변환하는 것은 호출자의 책임입니다.
널 포인터
모든 타입의 포인터는 해당 타입의 null 포인터 값 이라고 알려진 특별한 값을 가집니다. 값이 null인 포인터는 객체나 함수를 가리키지 않으며(null 포인터를 역참조하는 것은 정의되지 않은 동작입니다), 값이 동일하게 null 인 동일한 타입의 모든 포인터와 동등하게 비교됩니다.
포인터를 null로 초기화하거나 기존 포인터에 null 값을 할당하려면 null 포인터 상수( NULL 또는 값이 0인 다른 정수 상수)를 사용할 수 있습니다. 정적 초기화 또한 포인터를 해당 null 값으로 초기화합니다.
널 포인터는 객체의 부재를 나타내거나 다른 유형의 오류 조건을 나타내는 데 사용될 수 있습니다. 일반적으로, 포인터 인수를 받는 함수는 거의 항상 값이 널인지 확인하고 해당 경우를 다르게 처리해야 합니다(예를 들어, free 함수는 널 포인터가 전달될 때 아무 작업도 수행하지 않습니다).
참고 사항
객체에 대한 모든 포인터가 캐스트될 수 있지만 , 객체의 선언된 타입과 다른 타입에 대한 포인터를 역참조하는 것은 거의 항상 undefined behavior입니다. 자세한 내용은 strict aliasing 을 참조하십시오.
|
포인터를 통해 객체에 접근하는 함수에게 해당 포인터들이 별칭(alias)을 사용하지 않음을 표시할 수 있습니다. 자세한 내용은 restrict 를 참조하십시오. |
(C99부터) |
배열 타입의 lvalue 표현식은 대부분의 상황에서 사용될 때, 배열의 첫 번째 요소를 가리키는 포인터로 암시적 변환 을 겪습니다. 자세한 내용은 배열 을 참조하십시오.
char *str = "abc"; // "abc"는 char[4] 배열이며, str은 'a'를 가리키는 포인터입니다
char에 대한 포인터는 종종 문자열을 표현하는 데 사용됩니다 . 유효한 byte string을 표현하기 위해서는, 포인터가 char 배열의 요소인 char를 가리키고 있어야 하며, 포인터가 참조하는 요소의 인덱스보다 크거나 같은 위치에 값이 0인 char가 존재해야 합니다.
참고문헌
- C23 표준 (ISO/IEC 9899:2024):
-
- 6.7.6.1 포인터 선언자 (p: TBD)
- C17 표준 (ISO/IEC 9899:2018):
-
- 6.7.6.1 포인터 선언자 (p: 93-94)
- C11 표준 (ISO/IEC 9899:2011):
-
- 6.7.6.1 포인터 선언자 (p: 130)
- C99 표준 (ISO/IEC 9899:1999):
-
- 6.7.5.1 포인터 선언자 (p: 115-116)
- C89/C90 표준 (ISO/IEC 9899:1990):
-
- 3.5.4.1 포인터 선언자
참고 항목
|
C++ 문서
참조:
포인터 선언
|