C의 정적 함수


172

C에서 함수를 정적으로 만드는 요점은 무엇입니까?


7
@nightcracker : C ++에는 "methods"와 같은 것이 없습니다. Objective-C와 혼동한다고 생각합니다.
Bo Persson

1
아냐, 나는 파이썬과 혼동된다. 클래스 내부의 함수를 파이썬에서 메소드라고합니다.
orlp

답변:


212

함수를 만들면 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;
}

8
번역 단위가 여기에 사용되는 올바른 용어입니까? 파일이 더 정확하지 않습니까? 내가 이해 한 바에 따르면 정적 함수는 링커에서 숨겨져 있으며 링커는 변환 단위에서 작동하지 않습니다.
Steven Eckhoff

2
나는 또한 그것이 링커에 숨겨져 있다고 생각하고 싶다고 말했다. 그렇게 분명해 보입니다.
Steven Eckhoff

1
따라서 내부 함수 (c 파일 외부에서 호출하지 않아야 함) 정적 함수로 지정해야합니다. 따라서 다른 곳에서는 전화를 걸 수 없습니다. 감사합니다 :)
hqt

1
이것을 어떻게 컴파일합니까? 당신은 사용 #include <helper_file.c>합니까? 나는 그것이 단일 번역 단위가 될 것이라고 생각합니다 ...
Atcold

2
@Atcold : 내가 코드를 작성하는 방법은 단순히 다음과 같이 명령 행에 2 개의 소스 파일을 포함시킨다 gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c. 함수의 프로토 타입은 두 소스 파일 모두에 있으며 헤더 파일이 필요하지 않습니다. 링커가 기능을 해결합니다.
pmg

80

pmg 은 캡슐화에 대해 자리 잡고있다; 함수를 다른 변환 단위 (또는 그로 인해 ) 에서 숨기는 것 외에도 함수를 만들면 static컴파일러 최적화가있을 때 성능 이점을 얻을 수 있습니다.

static코드가 주소에 대한 포인터를 사용하지 않는 한 현재 변환 단위 외부 에서는 함수를 호출 할 수 없으므로 컴파일러는 모든 호출 지점을 제어합니다.

즉, 비표준 ABI를 자유롭게 사용하거나 완전히 인라인하거나 외부 연결 기능으로는 불가능한 다른 최적화를 수행 할 수 있습니다.


9
... 기능의 주소를 얻지 않는 한.
caf

1
@caf 함수의 주소가 무엇을 의미합니까? 나에게, 컴파일 시간에 주소가 있거나 주소가 할당되는 함수 / 변수의 개념은 약간 혼란 스럽습니다. 자세히 설명해 주시겠습니까?
SayeedHussain

2
@crypticcoder : 프로그램이 메모리에로드되므로 함수에도 메모리 위치가 있으며 주소를 얻을 수 있습니다. 함수 포인터를 사용하면 그 중 하나를 호출 할 수 있습니다. 그렇게하면 코드가 같은 위치에 그대로 있어야하므로 컴파일러가 수행 할 수있는 최적화 목록이 줄어 듭니다.

5
@ crypticcoder : 표현식이 함수에 대한 포인터를 평가하고 즉시 함수를 호출하는 것 이외의 기능을 수행한다는 것을 의미합니다. static함수에 대한 포인터가 현재 변환 단위를 이스케이프하면 해당 함수를 다른 변환 단위에서 직접 호출 할 수 있습니다.
caf

@caf 함수의 주소를 가져 오면 컴파일러가이를 감지하고이 답변에 언급 된 정적 함수 최적화를 해제합니까 (예 : 비표준 ABI 사용)? 나는 그것이 필요하다고 생각합니다.
sevko

28

staticC 의 키워드는 함수가 해당 파일에만 존재하도록 컴파일 된 파일 (.h와 반대로 .c)에서 사용됩니다.

일반적으로 함수를 만들 때 컴파일러는 링커가 함수 호출을 해당 함수에 연결하는 데 사용할 수있는 cruft를 생성합니다. static 키워드를 사용하면 링커에 의존하지 않고 수행 할 수 있기 때문에 동일한 파일 내의 다른 함수가이 함수를 호출 할 수 있지만 링커에는 다른 파일이 함수에 액세스하도록하는 정보가 없습니다.


1
3Doub : "크래프팅 (cruft)"이라는 단어를 사용하는 것이 당신이 생각하는 것보다 더 정확합니다. 질문의 맥락에서 "크래프팅 (cruft)"은 여기서 사용하기에 적합한 단어입니다.
Erik Aronesty

@ 3Doubloons 나는 그것이 단순화 된 것에 동의하지만 초보자를 위해 이해하기가 훨씬 쉽다고 생각합니다.
Ingo Bürk

11

위의 게시물을 보면 한 가지 세부 사항을 지적하고 싶습니다.

기본 파일 ( "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 파일에서 함수가 호출되지 않도록합니다.

내가 틀렸다면 나를 바로 잡으십시오.


5

C 프로그래머는 Java 및 C ++에서 공용 및 개인용 선언을 사용하는 것처럼 정적 속성을 사용하여 모듈 내에서 변수 및 함수 선언을 숨 깁니다. C 소스 파일은 모듈의 역할을합니다. 정적 속성으로 선언 된 모든 글로벌 변수 또는 함수는 해당 모듈 전용입니다. 마찬가지로 정적 속성없이 선언 된 전역 변수 또는 함수는 공용이며 다른 모듈에서 액세스 할 수 있습니다. 가능하면 정적 속성으로 변수와 함수를 보호하는 것이 좋습니다.


4

pmg의 답변은 매우 설득력이 있습니다. 정적 선언이 객체 수준에서 어떻게 작동하는지 알고 싶다면 아래 정보가 흥미로울 수 있습니다. pmg가 작성한 동일한 프로그램을 재사용하고 .so (공유 객체) 파일로 컴파일러

다음 내용은 .so 파일을 사람이 읽을 수있는 것으로 덤프 한 후입니다.

0000000000000675 f1 : f1 함수의 주소

000000000000068c f2 : f2 (staticc) 함수의 주소

함수 주소의 차이에 주목하십시오. 다른 주소로 선언 된 함수의 경우 f2가 멀리 또는 객체 파일의 다른 세그먼트에 있다는 것을 잘 알 수 있습니다.

링커는 PLT (Procedure linkage table) 및 GOT (Global offsets table)이라는 것을 사용하여에 액세스 할 수있는 심볼을 이해합니다.

이제 GOT와 PLT는 모든 주소를 마술처럼 묶고 동적 섹션에는 링커가 볼 수있는 모든 기능에 대한 정보가 들어 있다고 생각합니다.

.so 파일의 동적 섹션을 덤프하면 많은 항목이 있지만 f1f2 함수 에만 관심이 있습니다.

동적 섹션은 주소 0000000000000675 에서 f1 기능에 대한 항목 만 보유하고 f2 에는 없습니다 .

숫자 : 값 크기 유형 바인드 비스 Ndx 이름

 9: 0000000000000675    23 FUNC    GLOBAL DEFAULT   11 f1

그리고 그게 다야 !. 이것으로부터 링커는 f2 함수가 .so 파일의 동적 섹션에 없기 때문에 f2 함수 를 찾는 데 실패 할 것 입니다.


0

일부 함수에 대한 액세스를 제한해야하는 경우 함수를 정의하고 선언하는 동안 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 */

이 답변은별로 도움이되지 않습니다.
fiscblog
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.