C에서 함수를 정적으로 만드는 요점은 무엇입니까?
C에서 함수를 정적으로 만드는 요점은 무엇입니까?
답변:
함수를 만들면 static
다른 번역 단위에서 숨겨져 캡슐화를 제공하는 데 도움이됩니다 .
helper_file.c
int f1(int); /* prototype */
static int f2(int); /* prototype */
int f1(int foo) {
return f2(foo); /* ok, f2 is in the same translation unit */
/* (basically same .c file) as f1 */
}
int f2(int foo) {
return 42 + foo;
}
main.c :
int f1(int); /* prototype */
int f2(int); /* prototype */
int main(void) {
f1(10); /* ok, f1 is visible to the linker */
f2(12); /* nope, f2 is not visible to the linker */
return 0;
}
#include <helper_file.c>
합니까? 나는 그것이 단일 번역 단위가 될 것이라고 생각합니다 ...
gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c
. 함수의 프로토 타입은 두 소스 파일 모두에 있으며 헤더 파일이 필요하지 않습니다. 링커가 기능을 해결합니다.
pmg 은 캡슐화에 대해 자리 잡고있다; 함수를 다른 변환 단위 (또는 그로 인해 ) 에서 숨기는 것 외에도 함수를 만들면 static
컴파일러 최적화가있을 때 성능 이점을 얻을 수 있습니다.
static
코드가 주소에 대한 포인터를 사용하지 않는 한 현재 변환 단위 외부 에서는 함수를 호출 할 수 없으므로 컴파일러는 모든 호출 지점을 제어합니다.
즉, 비표준 ABI를 자유롭게 사용하거나 완전히 인라인하거나 외부 연결 기능으로는 불가능한 다른 최적화를 수행 할 수 있습니다.
static
함수에 대한 포인터가 현재 변환 단위를 이스케이프하면 해당 함수를 다른 변환 단위에서 직접 호출 할 수 있습니다.
static
C 의 키워드는 함수가 해당 파일에만 존재하도록 컴파일 된 파일 (.h와 반대로 .c)에서 사용됩니다.
일반적으로 함수를 만들 때 컴파일러는 링커가 함수 호출을 해당 함수에 연결하는 데 사용할 수있는 cruft를 생성합니다. static 키워드를 사용하면 링커에 의존하지 않고 수행 할 수 있기 때문에 동일한 파일 내의 다른 함수가이 함수를 호출 할 수 있지만 링커에는 다른 파일이 함수에 액세스하도록하는 정보가 없습니다.
위의 게시물을 보면 한 가지 세부 사항을 지적하고 싶습니다.
기본 파일 ( "main.c")이 다음과 같다고 가정합니다.
#include "header.h"
int main(void) {
FunctionInHeader();
}
이제 세 가지 경우를 고려하십시오.
사례 1 : 헤더 파일 ( "header.h")은 다음과 같습니다.
#include <stdio.h>
static void FunctionInHeader();
void FunctionInHeader() {
printf("Calling function inside header\n");
}
그런 다음 Linux에서 다음 명령을 수행하십시오.
gcc main.c header.h -o main
성공할 것이다 ! 다음 중 하나를 실행하면
./main
출력은
헤더 내부의 함수 호출
정적 함수가 인쇄해야하는 것은 다음과 같습니다.
사례 2 : 헤더 파일 ( "header.h")은 다음과 같습니다.
static void FunctionInHeader();
또한 "header.c"파일이 하나 더 있습니다.
#include <stdio.h>
#include "header.h"
void FunctionInHeader() {
printf("Calling function inside header\n");
}
그런 다음 다음 명령
gcc main.c header.h header.c -o main
오류가 발생합니다.
사례 3 :
헤더 파일 ( "header.h")이 다음을 제외하고 사례 2와 유사합니다.
void FunctionInHeader(); // keyword static removed
그런 다음 2와 동일한 명령이 성공하고 ./main을 추가로 실행하면 예상 결과가 나타납니다.
따라서이 테스트 (Acer x86 컴퓨터, Ubuntu OS에서 실행)에서 나는
static 키워드는 함수가 정의 된 위치가 아닌 다른 * .c 파일에서 함수가 호출되지 않도록합니다.
내가 틀렸다면 나를 바로 잡으십시오.
pmg의 답변은 매우 설득력이 있습니다. 정적 선언이 객체 수준에서 어떻게 작동하는지 알고 싶다면 아래 정보가 흥미로울 수 있습니다. pmg가 작성한 동일한 프로그램을 재사용하고 .so (공유 객체) 파일로 컴파일러
다음 내용은 .so 파일을 사람이 읽을 수있는 것으로 덤프 한 후입니다.
0000000000000675 f1 : f1 함수의 주소
000000000000068c f2 : f2 (staticc) 함수의 주소
함수 주소의 차이에 주목하십시오. 다른 주소로 선언 된 함수의 경우 f2가 멀리 또는 객체 파일의 다른 세그먼트에 있다는 것을 잘 알 수 있습니다.
링커는 PLT (Procedure linkage table) 및 GOT (Global offsets table)이라는 것을 사용하여에 액세스 할 수있는 심볼을 이해합니다.
이제 GOT와 PLT는 모든 주소를 마술처럼 묶고 동적 섹션에는 링커가 볼 수있는 모든 기능에 대한 정보가 들어 있다고 생각합니다.
.so 파일의 동적 섹션을 덤프하면 많은 항목이 있지만 f1 및 f2 함수 에만 관심이 있습니다.
동적 섹션은 주소 0000000000000675 에서 f1 기능에 대한 항목 만 보유하고 f2 에는 없습니다 .
숫자 : 값 크기 유형 바인드 비스 Ndx 이름
9: 0000000000000675 23 FUNC GLOBAL DEFAULT 11 f1
그리고 그게 다야 !. 이것으로부터 링커는 f2 함수가 .so 파일의 동적 섹션에 없기 때문에 f2 함수 를 찾는 데 실패 할 것 입니다.
일부 함수에 대한 액세스를 제한해야하는 경우 함수를 정의하고 선언하는 동안 static 키워드를 사용합니다.
/* file ab.c */
static void function1(void)
{
puts("function1 called");
}
And store the following code in another file ab1.c
/* file ab1.c */
int main(void)
{
function1();
getchar();
return 0;
}
/* in this code, we'll get a "Undefined reference to function1".Because function 1 is declared static in file ab.c and can't be used in ab1.c */