C Language
스토리지 클래스
수색…
소개
저장소 클래스는 변수 또는 함수의 범위를 설정하는 데 사용됩니다. 변수의 저장 클래스를 아는 것으로 프로그램의 실행 시간 동안 변수의 수명을 결정할 수 있습니다.
통사론
[auto | register | static | extern] <데이터 유형> <변수 이름> [= <값>];
[정적 _Thread_local | extern _Thread_local | _Thread_local] <데이터 형식> <변수 이름> [= <값>]; / * 이후> = C11 * /
예 :
typedef int foo ;
extern int foo [2];
비고
저장소 클래스 지정자는 최상위 수준의 선언 옆에 나타날 수있는 키워드입니다. 이러한 키워드를 사용하면 파일 범위 또는 블록 범위에서 선언되었는지 여부에 따라 선언 된 개체의 저장 기간 및 연결에 영향을줍니다.
예어 | 저장 기간 | 결합 | 비고 |
---|---|---|---|
static | 공전 | 내부의 | 파일 범위의 객체에 대한 내부 연결을 설정합니다. 블록 범위의 객체에 대한 정적 저장 기간을 설정합니다. |
extern | 공전 | 외부 | 파일 범위에 정의 된 객체에 대한 묵시적이며 따라서 중복되는 이니셜 라이저도 있습니다. 이니셜 라이저가없는 파일 범위의 선언에 사용되면 정의가 다른 변환 단위에서 발견되고 링크 타임에 해석 될 것이라는 암시를줍니다. |
auto | 오토매틱 | 부적절한 | 블록 범위에서 선언 된 객체에 대해서는 묵시적이며 따라서 중복됩니다. |
register | 오토매틱 | 부적절한 | 자동 저장 기간이있는 객체에만 적용됩니다. 변수가 레지스터에 저장되어야한다는 힌트를 제공합니다. 부과 된 제약 조건은 이러한 객체에서 단항 & "주소"연산자를 사용할 수 없으므로 객체에 별칭을 지정할 수 없다는 것입니다. |
typedef | 부적절한 | 부적절한 | 실제로 저장소 클래스 지정자는 아니지만 구문 론적 관점 에서처럼 작동합니다. 유일한 차이점은 선언 된 식별자가 객체가 아니라 유형이라는 점입니다. |
_Thread_local | 실 | 내부 / 외부 | 스레드 저장 기간 을 나타내는 C11에 도입되었습니다. 블록 범위에서 사용되는 경우 extern 또는 static 포함해야합니다. |
모든 개체는 이러한 키워드가 생략 된 경우에도 연결된 범위 (범위와 관계 없음) 및 연결 (파일 범위에서만 선언과 관련이 있음)이 있습니다.
최상위 유형 지정자 ( int
, unsigned
, short
등) 및 최상위 유형 한정자 ( const
, volatile
)에 대한 저장 클래스 지정자의 순서는 적용되지 않으므로 다음 두 선언이 모두 유효합니다.
int static const unsigned a = 5; /* bad practice */
static const unsigned int b = 5; /* good practice */
그러나 먼저 저장소 클래스 지정자를 입력 한 다음 모든 형식 한정자를 입력 한 다음 형식 지정자 ( void
, char
, int
, signed long
, unsigned long long
, long double
...)를 넣는 것이 좋습니다.
모든 저장소 클래스 지정자가 특정 범위에서 합법적 인 것은 아닙니다.
register int x; /* legal at block scope, illegal at file scope */
auto int y; /* same */
static int z; /* legal at both file and block scope */
extern int a; /* same */
extern int b = 5; /* legal and redundant at file scope, illegal at block scope */
/* legal because typedef is treated like a storage class specifier syntactically */
int typedef new_type_name;
저장 기간
저장 기간은 정적 또는 자동이 될 수 있습니다. 선언 된 객체의 경우 해당 범위와 저장 클래스 지정자에 따라 결정됩니다.
정적 저장 기간
정적 저장 기간이 변수는 프로그램의 전체 실행에 걸쳐 살고 파일 범위 (또는없이 모두를 선언 할 수 있습니다 static
)와 블록 범위에서 (넣어 static
명시 적으로). 일반적으로 프로그램 시작시 운영 체제에 의해 할당되고 초기화되며 프로세스가 종료 될 때 교정됩니다. 실제로 실행 파일 형식은 이러한 변수 ( data
, bss
및 rodata
)에 대한 전용 섹션을 포함하며 파일의 이러한 전체 섹션은 특정 범위의 메모리에 매핑됩니다.
스레드 저장 기간
이 저장 기간은 C11에서 도입되었습니다. 이전 C 표준에서는 사용할 수 없었습니다. 일부 컴파일러는 비슷한 의미를 갖는 비표준 확장을 제공합니다. 예를 들어, gcc는 _Thread_local
이없는 이전 C 표준에서 사용할 수있는 __thread
지정자를 지원합니다.
스레드 저장 기간이있는 변수는 파일 범위와 블록 범위 모두에서 선언 할 수 있습니다. 블록 범위에서 선언 된 경우 static
또는 extern
저장 지정자를 사용해야합니다. 수명은 생성 된 스레드 의 전체 실행입니다. 이것은 다른 저장 장치 지정자와 함께 나타날 수있는 유일한 저장 지정자입니다.
자동 저장 기간
자동 저장 기간이있는 변수는 블록 범위에서 (함수 내에서 직접 또는 함수 내 블록 내에서만) 선언 할 수 있습니다. 함수 또는 블록을 시작하고 떠날 때만 사용할 수 있습니다. 변수가 범위를 벗어나면 (함수에서 복귀하거나 블록을 벗어나서) 변수의 저장 공간이 자동으로 할당 해제됩니다. 포인터에서 동일한 변수에 대한 추가 참조는 유효하지 않으므로 정의되지 않은 동작이 발생합니다.
일반적인 구현에서 자동 변수는 함수의 스택 프레임이나 레지스터에있는 특정 오프셋에 있습니다.
외부 및 내부 연결
링키지는 파일 범위에서 선언 된 객체 (함수 및 변수)에만 관련이 있으며 여러 번역 단위에서의 가시성에 영향을줍니다. 외부 링크가있는 객체는 다른 모든 번역 단위에서 볼 수 있습니다 (적절한 선언이 포함되어있는 경우). 내부 링크가있는 객체는 다른 번역 단위에 노출되지 않으며 정의 된 변환 단위에서만 사용할 수 있습니다.
typedef
기존 유형을 기반으로 새 유형을 정의합니다. 이 구문은 변수 선언의 구문을 반영합니다.
/* Byte can be used wherever `unsigned char` is needed */
typedef unsigned char Byte;
/* Integer is the type used to declare an array consisting of a single int */
typedef int Integer[1];
/* NodeRef is a type used for pointers to a structure type with the tag "node" */
typedef struct node *NodeRef;
/* SigHandler is the function pointer type that gets passed to the signal function. */
typedef void (*SigHandler)(int);
기술적으로 저장소 클래스는 아니지만 컴파일러는 typedef
키워드가 사용되는 경우 다른 저장소 클래스가 허용되지 않으므로이를 하나로서 취급합니다.
typedef
는 중요하며 #define
매크로로 대체하면 안됩니다.
typedef int newType;
newType *ptr; // ptr is pointer to variable of type 'newType' aka int
하나,
#define int newType
newType *ptr; // Even though macros are exact replacements to words, this doesn't result to a pointer to variable of type 'newType' aka int
자동
이 저장 클래스는 식별자에 자동 저장 기간이 있음을 나타냅니다. 즉, 식별자가 정의 된 범위가 끝나면 식별자로 표시된 개체가 더 이상 유효하지 않습니다.
전역 범위에 있거나 static
으로 선언되지 않은 모든 객체는 기본적으로 자동 저장 기간이 정의되어 있으므로이 키워드는 주로 역사적인 관심 대상이므로 사용하면 안됩니다.
int foo(void)
{
/* An integer with automatic storage duration. */
auto int i = 3;
/* Same */
int j = 5;
return 0;
} /* The values of i and j are no longer able to be used. */
공전
static
저장소 클래스는 파일의 선언 위치에 따라 다른 용도로 사용됩니다.
식별자를 해당 번역 단위 에만 한정하려면 (scope = file).
/* No other translation unit can use this variable. */ static int i; /* Same; static is attached to the function type of f, not the return type int. */ static int f(int n);
함수의 다음 호출에서 사용할 데이터를 저장하려면 (scope = block) :
void foo() { static int a = 0; /* has static storage duration and its lifetime is the * entire execution of the program; initialized to 0 on * first function call */ int b = 0; /* b has block scope and has automatic storage duration and * only "exists" within function */ a += 10; b += 10; printf("static int a = %d, int b = %d\n", a, b); } int main(void) { int i; for (i = 0; i < 5; i++) { foo(); } return 0; }
이 코드는 다음을 인쇄합니다.
static int a = 10, int b = 10 static int a = 20, int b = 10 static int a = 30, int b = 10 static int a = 40, int b = 10 static int a = 50, int b = 10
정적 변수는 여러 스레드에서 호출 된 경우에도 해당 값을 유지합니다.
배열을 나타 내기 위해 함수 매개 변수에 사용되는 요소의 최소 수가 일정하고 null이 아닌 매개 변수가 있어야합니다.
/* a is expected to have at least 512 elements. */ void printInts(int a[static 512]) { size_t i; for (i = 0; i < 512; ++i) printf("%d\n", a[i]); }
필요한 항목 수 (또는 null이 아닌 포인터)는 컴파일러가 반드시 확인하지 않아도되며, 요소가 충분하지 않은 경우 컴파일러에서 어떤 방식 으로든 사용자에게 알릴 필요는 없습니다. 프로그래머가 512 개 요소 또는 널 포인터를 전달하면 정의되지 않은 동작이 결과입니다. 이것을 강제 할 수 없으므로 해당 매개 변수의 값을 해당 함수에 전달할 때 특별히주의해야합니다.
통근자
다른 곳에 정의 된 (또는 외부 링키지가있는 ) 객체 나 함수 를 선언하는 데 사용됩니다. 일반적으로 해당 객체 또는 함수가 정의되지 않은 모듈에서 사용할 객체 또는 함수를 선언하는 데 사용됩니다.
/* file1.c */
int foo = 2; /* Has external linkage since it is declared at file scope. */
/* file2.c */
#include <stdio.h>
int main(void)
{
/* `extern` keyword refers to external definition of `foo`. */
extern int foo;
printf("%d\n", foo);
return 0;
}
C99에서 inline
키워드를 사용하면 약간 재미있는 일이 생깁니다.
/* Should usually be place in a header file such that all users see the definition */
/* Hints to the compiler that the function `bar` might be inlined */
/* and suppresses the generation of an external symbol, unless stated otherwise. */
inline void bar(int drink)
{
printf("You ordered drink no.%d\n", drink);
}
/* To be found in just one .c file.
Creates an external function definition of `bar` for use by other files.
The compiler is allowed to choose between the inline version and the external
definition when `bar` is called. Without this line, `bar` would only be an inline
function, and other files would not be able to call it. */
extern void bar(int);
레지스터
객체에 대한 액세스가 가능한 한 빨리되어야한다는 것을 컴파일러에게 알려줍니다. 컴파일러가 실제로 힌트를 사용하는지 여부는 구현에 따라 결정됩니다. 단순히 auto
와 동등한 것으로 취급 할 수 있습니다.
register
로 선언 된 모든 객체에서 확실히 다른 유일한 속성은 계산 된 주소를 가질 수 없다는 것입니다. 따라서 register
는 특정 최적화를 보장하는 좋은 도구가 될 수 있습니다.
register size_t size = 467;
예기치 않게 변경 될 수있는 다른 기능으로 코드를 전달할 수있는 코드가 없기 때문에 절대로 별칭 을 지정할 수없는 개체입니다.
이 속성은 또한 배열
register int array[5];
첫 번째 요소에 대한 포인터로 감쇠 할 수없는 (즉, array
선회 &array[0]
). 즉, 이러한 배열의 요소에 액세스 할 수 없으며 배열 자체를 함수에 전달할 수 없습니다.
사실, register
저장 클래스로 선언 된 배열의 유일한 합법적 인 사용법은 sizeof
연산자입니다. 다른 연산자는 배열의 첫 번째 요소의 주소를 요구합니다. 이런 이유로 배열은 일반적으로 register
키워드로 선언해서는 안됩니다. 왜냐하면 배열 전체를 크기 계산하는 것 외에는 쓸모 없게 만듭니다. register
키워드없이 쉽게 할 수 있기 때문입니다.
register
저장 클래스는 블록 내부에서 정의되고 높은 빈도로 액세스되는 변수에 더 적합합니다. 예를 들어,
/* prints the sum of the first 5 integers*/
/* code assumed to be part of a function body*/
{
register int k, sum;
for(k = 1, sum = 0; k < 6; sum += k, k++);
printf("\t%d\n",sum);
}
_Alignof
연산자는 register
배열에도 사용할 수 있습니다.
_Thread_local
이것은 멀티 스레딩과 함께 C11에서 도입 된 새로운 스토리지 지정자입니다. 이전 C 표준에서는 사용할 수 없습니다.
스레드 저장 기간을 나타냅니다. _Thread_local
저장 지정자로 선언 된 변수는 객체가 해당 스레드에 대해 로컬 이고 해당 수명이 작성된 스레드의 전체 실행임을 나타냅니다. static
또는 extern
와 함께 나타날 수도 있습니다.
#include <threads.h>
#include <stdio.h>
#define SIZE 5
int thread_func(void *id)
{
/* thread local variable i. */
static _Thread_local int i;
/* Prints the ID passed from main() and the address of the i.
* Running this program will print different addresses for i, showing
* that they are all distinct objects. */
printf("From thread:[%d], Address of i (thread local): %p\n", *(int*)id, (void*)&i);
return 0;
}
int main(void)
{
thrd_t id[SIZE];
int arr[SIZE] = {1, 2, 3, 4, 5};
/* create 5 threads. */
for(int i = 0; i < SIZE; i++) {
thrd_create(&id[i], thread_func, &arr[i]);
}
/* wait for threads to complete. */
for(int i = 0; i < SIZE; i++) {
thrd_join(id[i], NULL);
}
}